blob: cc4bb42d1344d24e23f0290a4ebae99ca3a4d67f [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
41// Reference to a global config table, used all over the system.
42extern ConfigurationTable gConfig;
43
44
45/**@ The global alarms table. */
46//@{
47Mutex alarmsLock;
48list<string> alarmsList;
49void addAlarm(const string&);
50//@}
51
52
53
kurtis.heimerl5a872472013-05-31 21:47:25 +000054// (pat) If Log messages are printed before the classes in this module are inited
55// (which happens when static classes have constructors that do work)
56// the OpenBTS just crashes.
57// Prevent that by setting sLoggerInited to true when this module is inited.
58static bool sLoggerInited = 0;
59static struct CheckLoggerInitStatus {
60 CheckLoggerInitStatus() { sLoggerInited = 1; }
61} sCheckloggerInitStatus;
62
63
dburgess82c46ff2011-10-07 02:40:51 +000064
65/** Names of the logging levels. */
66const char *levelNames[] = {
67 "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
68};
69int numLevels = 8;
Alexander Chemeris57ef87d2015-05-24 13:23:11 -040070bool gLogToConsole = true;
71bool gLogToSyslog = false;
kurtis.heimerl5a872472013-05-31 21:47:25 +000072FILE *gLogToFile = NULL;
73Mutex gLogToLock;
dburgess82c46ff2011-10-07 02:40:51 +000074
75
kurtis.heimerl5a872472013-05-31 21:47:25 +000076int levelStringToInt(const string& name)
dburgess82c46ff2011-10-07 02:40:51 +000077{
78 // Reverse search, since the numerically larger levels are more common.
79 for (int i=numLevels-1; i>=0; i--) {
80 if (name == levelNames[i]) return i;
81 }
kurtis.heimerl5a872472013-05-31 21:47:25 +000082
83 // Common substitutions.
84 if (name=="INFORMATION") return 6;
85 if (name=="WARN") return 4;
86 if (name=="ERROR") return 3;
87 if (name=="CRITICAL") return 2;
88 if (name=="EMERGENCY") return 0;
89
90 // Unknown level.
91 return -1;
92}
93
94/** Given a string, return the corresponding level name. */
95int lookupLevel(const string& key)
96{
97 string val = gConfig.getStr(key);
98 int level = levelStringToInt(val);
99
100 if (level == -1) {
101 string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
102 level = levelStringToInt(defaultLevel);
103 _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
104 gConfig.set(key, defaultLevel);
105 }
106
107 return level;
dburgess82c46ff2011-10-07 02:40:51 +0000108}
109
110
111int getLoggingLevel(const char* filename)
112{
113 // Default level?
kurtis.heimerl5a872472013-05-31 21:47:25 +0000114 if (!filename) return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000115
116 // This can afford to be inefficient since it is not called that often.
117 const string keyName = string("Log.Level.") + string(filename);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000118 if (gConfig.defines(keyName)) return lookupLevel(keyName);
119 return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000120}
121
122
123
124int gGetLoggingLevel(const char* filename)
125{
126 // This is called a lot and needs to be efficient.
127
128 static Mutex sLogCacheLock;
129 static map<uint64_t,int> sLogCache;
130 static unsigned sCacheCount;
131 static const unsigned sCacheRefreshCount = 1000;
132
133 if (filename==NULL) return gGetLoggingLevel("");
134
135 HashString hs(filename);
136 uint64_t key = hs.hash();
137
138 sLogCacheLock.lock();
139 // Time for a cache flush?
140 if (sCacheCount>sCacheRefreshCount) {
141 sLogCache.clear();
142 sCacheCount=0;
143 }
144 // Is it cached already?
145 map<uint64_t,int>::const_iterator where = sLogCache.find(key);
146 sCacheCount++;
147 if (where!=sLogCache.end()) {
148 int retVal = where->second;
149 sLogCacheLock.unlock();
150 return retVal;
151 }
152 // Look it up in the config table and cache it.
153 // FIXME: Figure out why unlock and lock below fix the config table deadlock.
kurtis.heimerl5a872472013-05-31 21:47:25 +0000154 // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
dburgess82c46ff2011-10-07 02:40:51 +0000155 sLogCacheLock.unlock();
156 int level = getLoggingLevel(filename);
157 sLogCacheLock.lock();
158 sLogCache.insert(pair<uint64_t,int>(key,level));
159 sLogCacheLock.unlock();
160 return level;
161}
162
163
164
165
166
167// copies the alarm list and returns it. list supposed to be small.
168list<string> gGetLoggerAlarms()
169{
170 alarmsLock.lock();
171 list<string> ret;
172 // excuse the "complexity", but to use std::copy with a list you need
173 // an insert_iterator - copy technically overwrites, doesn't insert.
174 insert_iterator< list<string> > ii(ret, ret.begin());
175 copy(alarmsList.begin(), alarmsList.end(), ii);
176 alarmsLock.unlock();
177 return ret;
178}
179
180/** Add an alarm to the alarm list. */
181void addAlarm(const string& s)
182{
183 alarmsLock.lock();
184 alarmsList.push_back(s);
185 unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
186 while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
187 alarmsLock.unlock();
188}
189
190
191Log::~Log()
192{
dburgess7b2d5222011-11-20 00:22:41 +0000193 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000194 // Anything at or above LOG_CRIT is an "alarm".
195 // Save alarms in the local list and echo them to stderr.
Alexander Chemeriseefa8e52015-03-01 10:30:12 +0100196 if (mPriority <= LOG_ERR) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000197 if (sLoggerInited) addAlarm(mStream.str().c_str());
dburgess82c46ff2011-10-07 02:40:51 +0000198 cerr << mStream.str() << endl;
199 }
Alexander Chemeris57219202015-05-24 13:20:44 -0400200 // Current logging level was already checked by the macro. So just log.
201 // Log to syslog
202 if (gLogToSyslog) {
203 syslog(mPriority, "%s", mStream.str().c_str());
204 }
205 // Log to file and console
kurtis.heimerl5a872472013-05-31 21:47:25 +0000206 if (gLogToConsole||gLogToFile) {
207 int mlen = mStream.str().size();
208 int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
Alexander Chemerisbbef7e42015-06-04 00:09:29 -0400209 ScopedLock lock(gLogToLock);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000210 if (gLogToConsole) {
211 // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
212 // so just use std::cout.
213 std::cout << mStream.str();
214 if (neednl) std::cout<<"\n";
215 }
216 if (gLogToFile) {
217 fputs(mStream.str().c_str(),gLogToFile);
218 if (neednl) {fputc('\n',gLogToFile);}
219 fflush(gLogToFile);
220 }
kurtis.heimerl5a872472013-05-31 21:47:25 +0000221 }
dburgess82c46ff2011-10-07 02:40:51 +0000222}
223
224
dburgess7b2d5222011-11-20 00:22:41 +0000225Log::Log(const char* name, const char* level, int facility)
226{
227 mDummyInit = true;
228 gLogInit(name, level, facility);
229}
230
231
dburgess82c46ff2011-10-07 02:40:51 +0000232ostringstream& Log::get()
233{
234 assert(mPriority<numLevels);
235 mStream << levelNames[mPriority] << ' ';
236 return mStream;
237}
238
239
240
241void gLogInit(const char* name, const char* level, int facility)
242{
kurtis.heimerl5a872472013-05-31 21:47:25 +0000243 // Set the level if one has been specified.
dburgess82c46ff2011-10-07 02:40:51 +0000244 if (level) {
245 gConfig.set("Log.Level",level);
dburgess82c46ff2011-10-07 02:40:51 +0000246 }
247
kurtis.heimerl5a872472013-05-31 21:47:25 +0000248 // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
249 string str = gConfig.getStr("Log.File");
Alexander Chemeris57219202015-05-24 13:20:44 -0400250 if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000251 const char *fn = str.c_str();
252 if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
253 gLogToFile = fopen(fn,"w"); // New log file each time we start.
254 if (gLogToFile) {
255 time_t now;
256 time(&now);
257 fprintf(gLogToFile,"Starting at %s",ctime(&now));
258 fflush(gLogToFile);
259 std::cout << "Logging to file: " << fn << "\n";
260 }
261 }
dburgess82c46ff2011-10-07 02:40:51 +0000262 }
263
264 // Open the log connection.
265 openlog(name,0,facility);
266}
267
268
kurtis.heimerl00913d72012-12-16 06:08:18 +0000269void gLogEarly(int level, const char *fmt, ...)
270{
271 va_list args;
272
273 va_start(args, fmt);
274 vsyslog(level | LOG_USER, fmt, args);
275 va_end(args);
276}
dburgess82c46ff2011-10-07 02:40:51 +0000277
278// vim: ts=4 sw=4