blob: 06e91f6651b83c5cfd3a10f32089ff2683154e3f [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;
kurtis.heimerl5a872472013-05-31 21:47:25 +000070bool gLogToConsole = 0;
71FILE *gLogToFile = NULL;
72Mutex gLogToLock;
dburgess82c46ff2011-10-07 02:40:51 +000073
74
kurtis.heimerl5a872472013-05-31 21:47:25 +000075int levelStringToInt(const string& name)
dburgess82c46ff2011-10-07 02:40:51 +000076{
77 // Reverse search, since the numerically larger levels are more common.
78 for (int i=numLevels-1; i>=0; i--) {
79 if (name == levelNames[i]) return i;
80 }
kurtis.heimerl5a872472013-05-31 21:47:25 +000081
82 // Common substitutions.
83 if (name=="INFORMATION") return 6;
84 if (name=="WARN") return 4;
85 if (name=="ERROR") return 3;
86 if (name=="CRITICAL") return 2;
87 if (name=="EMERGENCY") return 0;
88
89 // Unknown level.
90 return -1;
91}
92
93/** Given a string, return the corresponding level name. */
94int lookupLevel(const string& key)
95{
96 string val = gConfig.getStr(key);
97 int level = levelStringToInt(val);
98
99 if (level == -1) {
100 string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
101 level = levelStringToInt(defaultLevel);
102 _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
103 gConfig.set(key, defaultLevel);
104 }
105
106 return level;
dburgess82c46ff2011-10-07 02:40:51 +0000107}
108
109
110int getLoggingLevel(const char* filename)
111{
112 // Default level?
kurtis.heimerl5a872472013-05-31 21:47:25 +0000113 if (!filename) return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000114
115 // This can afford to be inefficient since it is not called that often.
116 const string keyName = string("Log.Level.") + string(filename);
kurtis.heimerl5a872472013-05-31 21:47:25 +0000117 if (gConfig.defines(keyName)) return lookupLevel(keyName);
118 return lookupLevel("Log.Level");
dburgess82c46ff2011-10-07 02:40:51 +0000119}
120
121
122
123int gGetLoggingLevel(const char* filename)
124{
125 // This is called a lot and needs to be efficient.
126
127 static Mutex sLogCacheLock;
128 static map<uint64_t,int> sLogCache;
129 static unsigned sCacheCount;
130 static const unsigned sCacheRefreshCount = 1000;
131
132 if (filename==NULL) return gGetLoggingLevel("");
133
134 HashString hs(filename);
135 uint64_t key = hs.hash();
136
137 sLogCacheLock.lock();
138 // Time for a cache flush?
139 if (sCacheCount>sCacheRefreshCount) {
140 sLogCache.clear();
141 sCacheCount=0;
142 }
143 // Is it cached already?
144 map<uint64_t,int>::const_iterator where = sLogCache.find(key);
145 sCacheCount++;
146 if (where!=sLogCache.end()) {
147 int retVal = where->second;
148 sLogCacheLock.unlock();
149 return retVal;
150 }
151 // Look it up in the config table and cache it.
152 // FIXME: Figure out why unlock and lock below fix the config table deadlock.
kurtis.heimerl5a872472013-05-31 21:47:25 +0000153 // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
dburgess82c46ff2011-10-07 02:40:51 +0000154 sLogCacheLock.unlock();
155 int level = getLoggingLevel(filename);
156 sLogCacheLock.lock();
157 sLogCache.insert(pair<uint64_t,int>(key,level));
158 sLogCacheLock.unlock();
159 return level;
160}
161
162
163
164
165
166// copies the alarm list and returns it. list supposed to be small.
167list<string> gGetLoggerAlarms()
168{
169 alarmsLock.lock();
170 list<string> ret;
171 // excuse the "complexity", but to use std::copy with a list you need
172 // an insert_iterator - copy technically overwrites, doesn't insert.
173 insert_iterator< list<string> > ii(ret, ret.begin());
174 copy(alarmsList.begin(), alarmsList.end(), ii);
175 alarmsLock.unlock();
176 return ret;
177}
178
179/** Add an alarm to the alarm list. */
180void addAlarm(const string& s)
181{
182 alarmsLock.lock();
183 alarmsList.push_back(s);
184 unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
185 while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
186 alarmsLock.unlock();
187}
188
189
190Log::~Log()
191{
dburgess7b2d5222011-11-20 00:22:41 +0000192 if (mDummyInit) return;
dburgess82c46ff2011-10-07 02:40:51 +0000193 // Anything at or above LOG_CRIT is an "alarm".
194 // Save alarms in the local list and echo them to stderr.
195 if (mPriority <= LOG_CRIT) {
kurtis.heimerl5a872472013-05-31 21:47:25 +0000196 if (sLoggerInited) addAlarm(mStream.str().c_str());
dburgess82c46ff2011-10-07 02:40:51 +0000197 cerr << mStream.str() << endl;
198 }
199 // Current logging level was already checked by the macro.
200 // So just log.
201 syslog(mPriority, "%s", mStream.str().c_str());
kurtis.heimerl5a872472013-05-31 21:47:25 +0000202 // pat added for easy debugging.
203 if (gLogToConsole||gLogToFile) {
204 int mlen = mStream.str().size();
205 int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
206 gLogToLock.lock();
207 if (gLogToConsole) {
208 // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
209 // so just use std::cout.
210 std::cout << mStream.str();
211 if (neednl) std::cout<<"\n";
212 }
213 if (gLogToFile) {
214 fputs(mStream.str().c_str(),gLogToFile);
215 if (neednl) {fputc('\n',gLogToFile);}
216 fflush(gLogToFile);
217 }
218 gLogToLock.unlock();
219 }
dburgess82c46ff2011-10-07 02:40:51 +0000220}
221
222
dburgess7b2d5222011-11-20 00:22:41 +0000223Log::Log(const char* name, const char* level, int facility)
224{
225 mDummyInit = true;
226 gLogInit(name, level, facility);
227}
228
229
dburgess82c46ff2011-10-07 02:40:51 +0000230ostringstream& Log::get()
231{
232 assert(mPriority<numLevels);
233 mStream << levelNames[mPriority] << ' ';
234 return mStream;
235}
236
237
238
239void gLogInit(const char* name, const char* level, int facility)
240{
kurtis.heimerl5a872472013-05-31 21:47:25 +0000241 // Set the level if one has been specified.
dburgess82c46ff2011-10-07 02:40:51 +0000242 if (level) {
243 gConfig.set("Log.Level",level);
dburgess82c46ff2011-10-07 02:40:51 +0000244 }
245
kurtis.heimerl5a872472013-05-31 21:47:25 +0000246 // Pat added, tired of the syslog facility.
247 // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
248 string str = gConfig.getStr("Log.File");
249 if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
250 const char *fn = str.c_str();
251 if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
252 gLogToFile = fopen(fn,"w"); // New log file each time we start.
253 if (gLogToFile) {
254 time_t now;
255 time(&now);
256 fprintf(gLogToFile,"Starting at %s",ctime(&now));
257 fflush(gLogToFile);
258 std::cout << "Logging to file: " << fn << "\n";
259 }
260 }
dburgess82c46ff2011-10-07 02:40:51 +0000261 }
262
263 // Open the log connection.
264 openlog(name,0,facility);
265}
266
267
kurtis.heimerl00913d72012-12-16 06:08:18 +0000268void gLogEarly(int level, const char *fmt, ...)
269{
270 va_list args;
271
272 va_start(args, fmt);
273 vsyslog(level | LOG_USER, fmt, args);
274 va_end(args);
275}
dburgess82c46ff2011-10-07 02:40:51 +0000276
277// vim: ts=4 sw=4