syncing commonlibs with Many thanks to Michael Iedema for these patches, makes config a lot better.

git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@5655 19bc5d8c-e614-43d4-8b26-e1612bc8e597
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 @@
 
 
 
+// (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 @@
 	}
 	// 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 @@
 	// 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 @@
 
 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.