aboutsummaryrefslogtreecommitdiffstats
path: root/CommonLibs/Logger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'CommonLibs/Logger.cpp')
-rw-r--r--CommonLibs/Logger.cpp98
1 files changed, 81 insertions, 17 deletions
diff --git a/CommonLibs/Logger.cpp b/CommonLibs/Logger.cpp
index 36b7696..06e91f6 100644
--- a/CommonLibs/Logger.cpp
+++ b/CommonLibs/Logger.cpp
@@ -1,6 +1,7 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
+* Copyright 2011, 2012 Range Networks, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -32,6 +33,7 @@
#include "Configuration.h"
#include "Logger.h"
+#include "Threads.h" // pat added
using namespace std;
@@ -49,36 +51,71 @@ void addAlarm(const string&);
+// (pat) If Log messages are printed before the classes in this module are inited
+// (which happens when static classes have constructors that do work)
+// the OpenBTS just crashes.
+// Prevent that by setting sLoggerInited to true when this module is inited.
+static bool sLoggerInited = 0;
+static struct CheckLoggerInitStatus {
+ CheckLoggerInitStatus() { sLoggerInited = 1; }
+} sCheckloggerInitStatus;
+
+
/** Names of the logging levels. */
const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
+bool gLogToConsole = 0;
+FILE *gLogToFile = NULL;
+Mutex gLogToLock;
-/** Given a string, return the corresponding level name. */
-int lookupLevel(const string& name)
+int levelStringToInt(const string& name)
{
// Reverse search, since the numerically larger levels are more common.
for (int i=numLevels-1; i>=0; i--) {
if (name == levelNames[i]) return i;
}
- // This should never be called with a bogus name.
- LOG(ERR) << "undefined logging level " << name << "defaulting to ERR";
- return LOG_ERR;
+
+ // Common substitutions.
+ if (name=="INFORMATION") return 6;
+ if (name=="WARN") return 4;
+ if (name=="ERROR") return 3;
+ if (name=="CRITICAL") return 2;
+ if (name=="EMERGENCY") return 0;
+
+ // Unknown level.
+ return -1;
+}
+
+/** Given a string, return the corresponding level name. */
+int lookupLevel(const string& key)
+{
+ string val = gConfig.getStr(key);
+ int level = levelStringToInt(val);
+
+ if (level == -1) {
+ string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
+ level = levelStringToInt(defaultLevel);
+ _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
+ gConfig.set(key, defaultLevel);
+ }
+
+ return level;
}
int getLoggingLevel(const char* filename)
{
// Default level?
- if (!filename) return lookupLevel(gConfig.getStr("Log.Level"));
+ if (!filename) return lookupLevel("Log.Level");
// This can afford to be inefficient since it is not called that often.
const string keyName = string("Log.Level.") + string(filename);
- if (gConfig.defines(keyName)) return lookupLevel(gConfig.getStr(keyName));
- return lookupLevel(gConfig.getStr("Log.Level"));
+ if (gConfig.defines(keyName)) return lookupLevel(keyName);
+ return lookupLevel("Log.Level");
}
@@ -113,6 +150,7 @@ int gGetLoggingLevel(const char* filename)
}
// Look it up in the config table and cache it.
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
+ // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
sLogCacheLock.unlock();
int level = getLoggingLevel(filename);
sLogCacheLock.lock();
@@ -155,12 +193,30 @@ Log::~Log()
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_CRIT) {
- addAlarm(mStream.str().c_str());
+ if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro.
// So just log.
syslog(mPriority, "%s", mStream.str().c_str());
+ // pat added for easy debugging.
+ if (gLogToConsole||gLogToFile) {
+ int mlen = mStream.str().size();
+ int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
+ gLogToLock.lock();
+ if (gLogToConsole) {
+ // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
+ // so just use std::cout.
+ std::cout << mStream.str();
+ if (neednl) std::cout<<"\n";
+ }
+ if (gLogToFile) {
+ fputs(mStream.str().c_str(),gLogToFile);
+ if (neednl) {fputc('\n',gLogToFile);}
+ fflush(gLogToFile);
+ }
+ gLogToLock.unlock();
+ }
}
@@ -182,18 +238,26 @@ ostringstream& Log::get()
void gLogInit(const char* name, const char* level, int facility)
{
- // Set the level.
+ // Set the level if one has been specified.
if (level) {
gConfig.set("Log.Level",level);
- } else {
- if (!gConfig.defines("Log.Level")) {
- gConfig.set("Log.Level","WARNING");
- }
}
- // Define other logging parameters in the global config.
- if (!gConfig.defines("Log.Alarms.Max")) {
- gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
+ // Pat added, tired of the syslog facility.
+ // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
+ string str = gConfig.getStr("Log.File");
+ if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
+ const char *fn = str.c_str();
+ if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
+ gLogToFile = fopen(fn,"w"); // New log file each time we start.
+ if (gLogToFile) {
+ time_t now;
+ time(&now);
+ fprintf(gLogToFile,"Starting at %s",ctime(&now));
+ fflush(gLogToFile);
+ std::cout << "Logging to file: " << fn << "\n";
+ }
+ }
}
// Open the log connection.