blob: 4e4dbbc5f7838223d9894373afdfa0ae3c18e152 [file] [log] [blame]
dburgess82c46ff2011-10-07 02:40:51 +00001/*
2* Copyright 2009, 2010 Free Software Foundation, Inc.
3* Copyright 2010 Kestrel Signal Processing, Inc.
kurtis.heimerl5a872472013-05-31 21:47:25 +00004* Copyright 2011, 2012 Range Networks, Inc.
dburgess82c46ff2011-10-07 02:40:51 +00005*
6*
7* This software is distributed under the terms of the GNU Affero Public License.
8* See the COPYING file in the main directory for details.
9*
10* This use of this software may be subject to additional restrictions.
11* See the LEGAL file in the main directory for details.
12
13 This program is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Affero General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Affero General Public License for more details.
22
23 You should have received a copy of the GNU Affero General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26*/
27
28#include <string.h>
29#include <cstdio>
30#include <fstream>
31#include <string>
kurtis.heimerl00913d72012-12-16 06:08:18 +000032#include <stdarg.h>
Alexander Chemeris4793f462017-03-17 18:35:48 -070033#include <sys/time.h> // For gettimeofday
dburgess82c46ff2011-10-07 02:40:51 +000034
35#include "Configuration.h"
36#include "Logger.h"
kurtis.heimerl5a872472013-05-31 21:47:25 +000037#include "Threads.h" // pat added
dburgess82c46ff2011-10-07 02:40:51 +000038
39
40using namespace std;
41
Alexander Chemerisa8cf2082015-07-30 20:04:18 -040042// Switches to enable/disable logging targets
43// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
44bool gLogToConsole = true;
45bool gLogToSyslog = false;
46FILE *gLogToFile = NULL;
47Mutex gLogToLock;
48
49
dburgess82c46ff2011-10-07 02:40:51 +000050// Reference to a global config table, used all over the system.
51extern ConfigurationTable gConfig;
52
53
54/**@ The global alarms table. */
55//@{
56Mutex alarmsLock;
57list<string> alarmsList;
58void addAlarm(const string&);
59//@}
60
61
62
kurtis.heimerl5a872472013-05-31 21:47:25 +000063// (pat) If Log messages are printed before the classes in this module are inited
64// (which happens when static classes have constructors that do work)
65// the OpenBTS just crashes.
66// Prevent that by setting sLoggerInited to true when this module is inited.
67static bool sLoggerInited = 0;
68static struct CheckLoggerInitStatus {
69 CheckLoggerInitStatus() { sLoggerInited = 1; }
70} sCheckloggerInitStatus;
71
72
dburgess82c46ff2011-10-07 02:40:51 +000073
74/** Names of the logging levels. */
75const char *levelNames[] = {
76 "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
77};
78int numLevels = 8;
79
80
kurtis.heimerl5a872472013-05-31 21:47:25 +000081int levelStringToInt(const string& name)
dburgess82c46ff2011-10-07 02:40:51 +000082{
83 // Reverse search, since the numerically larger levels are more common.
84 for (int i=numLevels-1; i>=0; i--) {
85 if (name == levelNames[i]) return i;
86 }
kurtis.heimerl5a872472013-05-31 21:47:25 +000087
88 // Common substitutions.
89 if (name=="INFORMATION") return 6;
90 if (name=="WARN") return 4;
91 if (name=="ERROR") return 3;
92 if (name=="CRITICAL") return 2;
93 if (name=="EMERGENCY") return 0;
94
95 // Unknown level.
96 return -1;
97}
98
99/** Given a string, return the corresponding level name. */
100int lookupLevel(const string& key)
101{
102 string val = gConfig.getStr(key);
103 int level = levelStringToInt(val);
104
105 if (level == -1) {
106 string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
107 level = levelStringToInt(defaultLevel);
108 _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
109 gConfig.set(key, defaultLevel);
110 }
111
112 return level;
dburgess82c46ff2011-10-07 02:40:51 +0000113}
114
Alexander Chemeris4793f462017-03-17 18:35:48 -0700115static std::string format(const char *fmt, ...)
116{
117 va_list ap;
118 char buf[300];
119 va_start(ap,fmt);
120 int n = vsnprintf(buf,300,fmt,ap);
121 va_end(ap);
122 if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
123 return std::string(buf);
124}
125
126const std::string timestr()
127{
128 struct timeval tv;
129 struct tm tm;
130 gettimeofday(&tv,NULL);
131 localtime_r(&tv.tv_sec,&tm);
132 unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
133 return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
134}
135
136std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
137{
138 return os << ss.str();
139}
dburgess82c46ff2011-10-07 02:40:51 +0000140
141int getLoggingLevel(const char* filename)
142{
143 // Default level?
kurtis.heimerl5a872472013-05-31 21:47:25 +0000144 if (!filename) return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000145
146 // This can afford to be inefficient since it is not called that often.
147 const string keyName = string("Log.Level.") + string(filename);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000148 if (gConfig.defines(keyName)) return lookupLevel(keyName);
149 return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000150}
151
152
153
154int gGetLoggingLevel(const char* filename)
155{
156 // This is called a lot and needs to be efficient.
157
158 static Mutex sLogCacheLock;
159 static map<uint64_t,int> sLogCache;
160 static unsigned sCacheCount;
161 static const unsigned sCacheRefreshCount = 1000;
162
163 if (filename==NULL) return gGetLoggingLevel("");
164
165 HashString hs(filename);
166 uint64_t key = hs.hash();
167
168 sLogCacheLock.lock();
169 // Time for a cache flush?
170 if (sCacheCount>sCacheRefreshCount) {
171 sLogCache.clear();
172 sCacheCount=0;
173 }
174 // Is it cached already?
175 map<uint64_t,int>::const_iterator where = sLogCache.find(key);
176 sCacheCount++;
177 if (where!=sLogCache.end()) {
178 int retVal = where->second;
179 sLogCacheLock.unlock();
180 return retVal;
181 }
182 // Look it up in the config table and cache it.
183 // FIXME: Figure out why unlock and lock below fix the config table deadlock.
kurtis.heimerl5a872472013-05-31 21:47:25 +0000184 // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
dburgess82c46ff2011-10-07 02:40:51 +0000185 sLogCacheLock.unlock();
186 int level = getLoggingLevel(filename);
187 sLogCacheLock.lock();
188 sLogCache.insert(pair<uint64_t,int>(key,level));
189 sLogCacheLock.unlock();
190 return level;
191}
192
193
194
195
196
197// copies the alarm list and returns it. list supposed to be small.
198list<string> gGetLoggerAlarms()
199{
200 alarmsLock.lock();
201 list<string> ret;
202 // excuse the "complexity", but to use std::copy with a list you need
203 // an insert_iterator - copy technically overwrites, doesn't insert.
204 insert_iterator< list<string> > ii(ret, ret.begin());
205 copy(alarmsList.begin(), alarmsList.end(), ii);
206 alarmsLock.unlock();
207 return ret;
208}
209
210/** Add an alarm to the alarm list. */
211void addAlarm(const string& s)
212{
213 alarmsLock.lock();
214 alarmsList.push_back(s);
215 unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
216 while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
217 alarmsLock.unlock();
218}
219
220
221Log::~Log()
222{
dburgess7b2d5222011-11-20 00:22:41 +0000223 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000224 // Anything at or above LOG_CRIT is an "alarm".
225 // Save alarms in the local list and echo them to stderr.
Alexander Chemeriscc6f79b2015-03-01 10:30:12 +0100226 if (mPriority <= LOG_ERR) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000227 if (sLoggerInited) addAlarm(mStream.str().c_str());
dburgess82c46ff2011-10-07 02:40:51 +0000228 cerr << mStream.str() << endl;
229 }
Alexander Chemeris57219202015-05-24 13:20:44 -0400230 // Current logging level was already checked by the macro. So just log.
231 // Log to syslog
232 if (gLogToSyslog) {
233 syslog(mPriority, "%s", mStream.str().c_str());
234 }
235 // Log to file and console
kurtis.heimerl5a872472013-05-31 21:47:25 +0000236 if (gLogToConsole||gLogToFile) {
237 int mlen = mStream.str().size();
238 int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
Alexander Chemeris909ffbf2015-06-04 00:09:29 -0400239 ScopedLock lock(gLogToLock);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000240 if (gLogToConsole) {
241 // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
242 // so just use std::cout.
243 std::cout << mStream.str();
244 if (neednl) std::cout<<"\n";
245 }
246 if (gLogToFile) {
247 fputs(mStream.str().c_str(),gLogToFile);
248 if (neednl) {fputc('\n',gLogToFile);}
249 fflush(gLogToFile);
250 }
kurtis.heimerl5a872472013-05-31 21:47:25 +0000251 }
dburgess82c46ff2011-10-07 02:40:51 +0000252}
253
254
dburgess7b2d5222011-11-20 00:22:41 +0000255Log::Log(const char* name, const char* level, int facility)
256{
257 mDummyInit = true;
258 gLogInit(name, level, facility);
259}
260
261
dburgess82c46ff2011-10-07 02:40:51 +0000262ostringstream& Log::get()
263{
264 assert(mPriority<numLevels);
265 mStream << levelNames[mPriority] << ' ';
266 return mStream;
267}
268
269
270
271void gLogInit(const char* name, const char* level, int facility)
272{
kurtis.heimerl5a872472013-05-31 21:47:25 +0000273 // Set the level if one has been specified.
dburgess82c46ff2011-10-07 02:40:51 +0000274 if (level) {
275 gConfig.set("Log.Level",level);
dburgess82c46ff2011-10-07 02:40:51 +0000276 }
277
kurtis.heimerl5a872472013-05-31 21:47:25 +0000278 // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
279 string str = gConfig.getStr("Log.File");
Alexander Chemeris57219202015-05-24 13:20:44 -0400280 if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000281 const char *fn = str.c_str();
282 if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
283 gLogToFile = fopen(fn,"w"); // New log file each time we start.
284 if (gLogToFile) {
285 time_t now;
286 time(&now);
287 fprintf(gLogToFile,"Starting at %s",ctime(&now));
288 fflush(gLogToFile);
289 std::cout << "Logging to file: " << fn << "\n";
290 }
291 }
dburgess82c46ff2011-10-07 02:40:51 +0000292 }
293
294 // Open the log connection.
295 openlog(name,0,facility);
296}
297
298
kurtis.heimerl00913d72012-12-16 06:08:18 +0000299void gLogEarly(int level, const char *fmt, ...)
300{
301 va_list args;
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400302
kurtis.heimerl00913d72012-12-16 06:08:18 +0000303 va_start(args, fmt);
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400304
305 if (gLogToSyslog) {
306 va_list args_copy;
307 va_copy(args_copy, args);
308 vsyslog(level | LOG_USER, fmt, args_copy);
309 va_end(args_copy);
310 }
311
312 if (gLogToConsole) {
313 va_list args_copy;
314 va_copy(args_copy, args);
315 vprintf(fmt, args_copy);
316 printf("\n");
317 va_end(args_copy);
318 }
319
320 if (gLogToFile) {
321 va_list args_copy;
322 va_copy(args_copy, args);
323 vfprintf(gLogToFile, fmt, args_copy);
324 fprintf(gLogToFile, "\n");
325 va_end(args_copy);
326 }
327
kurtis.heimerl00913d72012-12-16 06:08:18 +0000328 va_end(args);
329}
dburgess82c46ff2011-10-07 02:40:51 +0000330
331// vim: ts=4 sw=4