blob: 82391cc2c905b49f1ae0619f85890fe538fe0e13 [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>
dburgess82c46ff2011-10-07 02:40:51 +000033
34#include "Configuration.h"
35#include "Logger.h"
kurtis.heimerl5a872472013-05-31 21:47:25 +000036#include "Threads.h" // pat added
dburgess82c46ff2011-10-07 02:40:51 +000037
38
39using namespace std;
40
Alexander Chemerisa8cf2082015-07-30 20:04:18 -040041// Switches to enable/disable logging targets
42// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
43bool gLogToConsole = true;
44bool gLogToSyslog = false;
45FILE *gLogToFile = NULL;
46Mutex gLogToLock;
47
48
dburgess82c46ff2011-10-07 02:40:51 +000049// Reference to a global config table, used all over the system.
50extern ConfigurationTable gConfig;
51
52
53/**@ The global alarms table. */
54//@{
55Mutex alarmsLock;
56list<string> alarmsList;
57void addAlarm(const string&);
58//@}
59
60
61
kurtis.heimerl5a872472013-05-31 21:47:25 +000062// (pat) If Log messages are printed before the classes in this module are inited
63// (which happens when static classes have constructors that do work)
64// the OpenBTS just crashes.
65// Prevent that by setting sLoggerInited to true when this module is inited.
66static bool sLoggerInited = 0;
67static struct CheckLoggerInitStatus {
68 CheckLoggerInitStatus() { sLoggerInited = 1; }
69} sCheckloggerInitStatus;
70
71
dburgess82c46ff2011-10-07 02:40:51 +000072
73/** Names of the logging levels. */
74const char *levelNames[] = {
75 "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
76};
77int numLevels = 8;
78
79
kurtis.heimerl5a872472013-05-31 21:47:25 +000080int levelStringToInt(const string& name)
dburgess82c46ff2011-10-07 02:40:51 +000081{
82 // Reverse search, since the numerically larger levels are more common.
83 for (int i=numLevels-1; i>=0; i--) {
84 if (name == levelNames[i]) return i;
85 }
kurtis.heimerl5a872472013-05-31 21:47:25 +000086
87 // Common substitutions.
88 if (name=="INFORMATION") return 6;
89 if (name=="WARN") return 4;
90 if (name=="ERROR") return 3;
91 if (name=="CRITICAL") return 2;
92 if (name=="EMERGENCY") return 0;
93
94 // Unknown level.
95 return -1;
96}
97
98/** Given a string, return the corresponding level name. */
99int lookupLevel(const string& key)
100{
101 string val = gConfig.getStr(key);
102 int level = levelStringToInt(val);
103
104 if (level == -1) {
105 string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
106 level = levelStringToInt(defaultLevel);
107 _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
108 gConfig.set(key, defaultLevel);
109 }
110
111 return level;
dburgess82c46ff2011-10-07 02:40:51 +0000112}
113
114
115int getLoggingLevel(const char* filename)
116{
117 // Default level?
kurtis.heimerl5a872472013-05-31 21:47:25 +0000118 if (!filename) return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000119
120 // This can afford to be inefficient since it is not called that often.
121 const string keyName = string("Log.Level.") + string(filename);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000122 if (gConfig.defines(keyName)) return lookupLevel(keyName);
123 return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000124}
125
126
127
128int gGetLoggingLevel(const char* filename)
129{
130 // This is called a lot and needs to be efficient.
131
132 static Mutex sLogCacheLock;
133 static map<uint64_t,int> sLogCache;
134 static unsigned sCacheCount;
135 static const unsigned sCacheRefreshCount = 1000;
136
137 if (filename==NULL) return gGetLoggingLevel("");
138
139 HashString hs(filename);
140 uint64_t key = hs.hash();
141
142 sLogCacheLock.lock();
143 // Time for a cache flush?
144 if (sCacheCount>sCacheRefreshCount) {
145 sLogCache.clear();
146 sCacheCount=0;
147 }
148 // Is it cached already?
149 map<uint64_t,int>::const_iterator where = sLogCache.find(key);
150 sCacheCount++;
151 if (where!=sLogCache.end()) {
152 int retVal = where->second;
153 sLogCacheLock.unlock();
154 return retVal;
155 }
156 // Look it up in the config table and cache it.
157 // FIXME: Figure out why unlock and lock below fix the config table deadlock.
kurtis.heimerl5a872472013-05-31 21:47:25 +0000158 // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
dburgess82c46ff2011-10-07 02:40:51 +0000159 sLogCacheLock.unlock();
160 int level = getLoggingLevel(filename);
161 sLogCacheLock.lock();
162 sLogCache.insert(pair<uint64_t,int>(key,level));
163 sLogCacheLock.unlock();
164 return level;
165}
166
167
168
169
170
171// copies the alarm list and returns it. list supposed to be small.
172list<string> gGetLoggerAlarms()
173{
174 alarmsLock.lock();
175 list<string> ret;
176 // excuse the "complexity", but to use std::copy with a list you need
177 // an insert_iterator - copy technically overwrites, doesn't insert.
178 insert_iterator< list<string> > ii(ret, ret.begin());
179 copy(alarmsList.begin(), alarmsList.end(), ii);
180 alarmsLock.unlock();
181 return ret;
182}
183
184/** Add an alarm to the alarm list. */
185void addAlarm(const string& s)
186{
187 alarmsLock.lock();
188 alarmsList.push_back(s);
189 unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
190 while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
191 alarmsLock.unlock();
192}
193
194
195Log::~Log()
196{
dburgess7b2d5222011-11-20 00:22:41 +0000197 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000198 // Anything at or above LOG_CRIT is an "alarm".
199 // Save alarms in the local list and echo them to stderr.
Alexander Chemeriscc6f79b2015-03-01 10:30:12 +0100200 if (mPriority <= LOG_ERR) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000201 if (sLoggerInited) addAlarm(mStream.str().c_str());
dburgess82c46ff2011-10-07 02:40:51 +0000202 cerr << mStream.str() << endl;
203 }
Alexander Chemeris57219202015-05-24 13:20:44 -0400204 // Current logging level was already checked by the macro. So just log.
205 // Log to syslog
206 if (gLogToSyslog) {
207 syslog(mPriority, "%s", mStream.str().c_str());
208 }
209 // Log to file and console
kurtis.heimerl5a872472013-05-31 21:47:25 +0000210 if (gLogToConsole||gLogToFile) {
211 int mlen = mStream.str().size();
212 int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
Alexander Chemeris909ffbf2015-06-04 00:09:29 -0400213 ScopedLock lock(gLogToLock);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000214 if (gLogToConsole) {
215 // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
216 // so just use std::cout.
217 std::cout << mStream.str();
218 if (neednl) std::cout<<"\n";
219 }
220 if (gLogToFile) {
221 fputs(mStream.str().c_str(),gLogToFile);
222 if (neednl) {fputc('\n',gLogToFile);}
223 fflush(gLogToFile);
224 }
kurtis.heimerl5a872472013-05-31 21:47:25 +0000225 }
dburgess82c46ff2011-10-07 02:40:51 +0000226}
227
228
dburgess7b2d5222011-11-20 00:22:41 +0000229Log::Log(const char* name, const char* level, int facility)
230{
231 mDummyInit = true;
232 gLogInit(name, level, facility);
233}
234
235
dburgess82c46ff2011-10-07 02:40:51 +0000236ostringstream& Log::get()
237{
238 assert(mPriority<numLevels);
239 mStream << levelNames[mPriority] << ' ';
240 return mStream;
241}
242
243
244
245void gLogInit(const char* name, const char* level, int facility)
246{
kurtis.heimerl5a872472013-05-31 21:47:25 +0000247 // Set the level if one has been specified.
dburgess82c46ff2011-10-07 02:40:51 +0000248 if (level) {
249 gConfig.set("Log.Level",level);
dburgess82c46ff2011-10-07 02:40:51 +0000250 }
251
kurtis.heimerl5a872472013-05-31 21:47:25 +0000252 // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
253 string str = gConfig.getStr("Log.File");
Alexander Chemeris57219202015-05-24 13:20:44 -0400254 if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000255 const char *fn = str.c_str();
256 if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
257 gLogToFile = fopen(fn,"w"); // New log file each time we start.
258 if (gLogToFile) {
259 time_t now;
260 time(&now);
261 fprintf(gLogToFile,"Starting at %s",ctime(&now));
262 fflush(gLogToFile);
263 std::cout << "Logging to file: " << fn << "\n";
264 }
265 }
dburgess82c46ff2011-10-07 02:40:51 +0000266 }
267
268 // Open the log connection.
269 openlog(name,0,facility);
270}
271
272
kurtis.heimerl00913d72012-12-16 06:08:18 +0000273void gLogEarly(int level, const char *fmt, ...)
274{
275 va_list args;
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400276
kurtis.heimerl00913d72012-12-16 06:08:18 +0000277 va_start(args, fmt);
Alexander Chemerisa8cf2082015-07-30 20:04:18 -0400278
279 if (gLogToSyslog) {
280 va_list args_copy;
281 va_copy(args_copy, args);
282 vsyslog(level | LOG_USER, fmt, args_copy);
283 va_end(args_copy);
284 }
285
286 if (gLogToConsole) {
287 va_list args_copy;
288 va_copy(args_copy, args);
289 vprintf(fmt, args_copy);
290 printf("\n");
291 va_end(args_copy);
292 }
293
294 if (gLogToFile) {
295 va_list args_copy;
296 va_copy(args_copy, args);
297 vfprintf(gLogToFile, fmt, args_copy);
298 fprintf(gLogToFile, "\n");
299 va_end(args_copy);
300 }
301
kurtis.heimerl00913d72012-12-16 06:08:18 +0000302 va_end(args);
303}
dburgess82c46ff2011-10-07 02:40:51 +0000304
305// vim: ts=4 sw=4