diff options
Diffstat (limited to 'CommonLibs/Logger.cpp')
-rw-r--r-- | CommonLibs/Logger.cpp | 98 |
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. |