rivet is hosted by Hepforge, IPPP Durham
Logging.cc
Go to the documentation of this file.
00001 #include "Rivet/Tools/Logging.hh"
00002 #include <ctime>
00003 #include <unistd.h>
00004 using namespace std;
00005 
00006 namespace Rivet {
00007 
00008 
00009   Log::LogMap Log::existingLogs;
00010   Log::LevelMap Log::defaultLevels;
00011   Log::ColorCodes Log::colorCodes;
00012   string Log::endColorCode;
00013   bool Log::showTimestamp = false;
00014   bool Log::showLogLevel = true;
00015   bool Log::showLoggerName = true;
00016   bool Log::useShellColors = true;
00017 
00018 
00019   Log::Log(const string& name)
00020     : _name(name), _level(INFO), _nostream(new ostream(0)) { }
00021 
00022 
00023   Log::Log(const string& name, int level)
00024     : _name(name), _level(level), _nostream(new ostream(0)) { }
00025 
00026 
00027   /// @todo Add single static setLevel
00028   void _updateLevels(const Log::LevelMap& defaultLevels, Log::LogMap& existingLogs) {
00029     /// @todo Check ordering - "Foo" should come before "Foo.Bar"
00030     for (Log::LevelMap::const_iterator lev = defaultLevels.begin(); lev != defaultLevels.end(); ++lev) {
00031       for (Log::LogMap::iterator log = existingLogs.begin(); log != existingLogs.end(); ++log) {
00032         if (log->first.find(lev->first) == 0) {
00033           log->second->setLevel(lev->second);
00034         }
00035       }
00036     }
00037   }
00038 
00039 
00040   void Log::setLevel(const string& name, int level) {
00041     defaultLevels[name] = level;
00042     //cout << name << " -> " << level << endl;
00043     _updateLevels(defaultLevels, existingLogs);
00044   }
00045 
00046 
00047   void Log::setLevels(const LevelMap& logLevels) {
00048     for (LevelMap::const_iterator lev = logLevels.begin(); lev != logLevels.end(); ++lev) {
00049       defaultLevels[lev->first] = lev->second;
00050     }
00051     _updateLevels(defaultLevels, existingLogs);
00052   }
00053 
00054 
00055   Log& Log::getLog(const string& name) {
00056     if (existingLogs.find(name) == existingLogs.end()) {
00057       int level = INFO;
00058       // Try running through all parent classes to find an existing level
00059       string tmpname = name;
00060       bool triedAllParents = false;
00061       while (! triedAllParents) {
00062         // Is there a default level?
00063         if (defaultLevels.find(tmpname) != defaultLevels.end()) {
00064           level = defaultLevels.find(tmpname)->second;
00065           break;
00066         }
00067         // Is there already such a logger? (NB. tmpname != name)
00068         if (existingLogs.find(tmpname) != existingLogs.end()) {
00069           level = existingLogs.find(tmpname)->second->getLevel();
00070           break;
00071         }
00072         // Crop the string back to the next parent level
00073         size_t lastDot = tmpname.find_last_of(".");
00074         if (lastDot != string::npos) {
00075           tmpname = tmpname.substr(0, lastDot);
00076         } else {
00077           triedAllParents = true;
00078         }
00079       }
00080       // for (LevelMap::const_iterator l = defaultLevels.begin(); l != defaultLevels.end(); ++l) {
00081       // 
00082       // }
00083       existingLogs[name] = new Log(name, level);
00084     }
00085     return *existingLogs[name];
00086   }
00087 
00088 
00089   string Log::getLevelName(int level) {
00090     /// @todo Do the map::upper_limit thing to find nearest level...
00091     switch(level) {
00092     case TRACE:
00093       return "TRACE";
00094     case DEBUG:
00095       return "DEBUG";
00096     case INFO:
00097       return "INFO";
00098     case WARN:
00099       return "WARN";
00100     case ERROR:
00101       return "ERROR";
00102     default:
00103       return "";
00104     }
00105     //throw Error("Enum value was not a valid log level. How did that happen?");
00106   }
00107 
00108 
00109   string Log::getColorCode(int level) {
00110     if (!Log::useShellColors) return "";
00111     // If the codes haven't been initialized, do so now.
00112     if (Log::colorCodes.empty()) {
00113       // If stdout is a valid tty, try to use the appropriate codes.
00114       if (isatty(1)) {
00115         /// @todo Test for VT100 compliance?
00116         Log::colorCodes[TRACE] = "\033[0;36m";
00117         Log::colorCodes[DEBUG] = "\033[0;34m";
00118         Log::colorCodes[INFO]  = "\033[0;32m";
00119         Log::colorCodes[WARN]  = "\033[0;33m";
00120         Log::colorCodes[ERROR] = "\033[0;31m";
00121         Log::endColorCode      = "\033[0m";
00122       } else {
00123         Log::colorCodes[TRACE] = "";
00124         Log::colorCodes[DEBUG] = "";
00125         Log::colorCodes[INFO] = "";
00126         Log::colorCodes[WARN] = "";
00127         Log::colorCodes[ERROR] = "";
00128       }
00129     }
00130     // Return the appropriate code from the colour map.
00131     /// @todo Do the map::upper_limit thing to find nearest level...
00132     return colorCodes[level];
00133   }
00134 
00135 
00136   Log::Level Log::getLevelFromName(const string& level) {
00137     if (level == "TRACE") return TRACE;
00138     if (level == "DEBUG") return DEBUG;
00139     if (level == "INFO") return INFO;
00140     if (level == "WARN") return WARN;
00141     if (level == "ERROR") return ERROR;
00142     throw Error("Couldn't create a log level from string '" + level + "'");
00143   }
00144 
00145 
00146   string Log::formatMessage(int level, const string& message) {
00147     string out;
00148     if (Log::useShellColors) {
00149       out += getColorCode(level);
00150     }
00151 
00152     if (Log::showLoggerName) {
00153       out += getName();
00154       out += ": ";
00155     }
00156 
00157     if (Log::showLogLevel) {
00158       out += Log::getLevelName(level);
00159       out += " ";
00160     }
00161 
00162     if (Log::showTimestamp) {
00163       time_t rawtime;
00164       time(&rawtime);
00165       char* timestr = ctime(&rawtime);
00166       timestr[24] = ' ';
00167       out += timestr;
00168       out += " ";
00169     }
00170 
00171     if (Log::useShellColors) {
00172       out += endColorCode;
00173     }
00174 
00175     out += " ";
00176     out += message;
00177  
00178     return out;
00179   }
00180 
00181 
00182   void Log::log(int level, const string& message) {
00183     if (isActive(level)) {
00184       cout << formatMessage(level, message) << endl;
00185     }
00186   }
00187 
00188 
00189   ostream& operator<<(Log& log, int level) {
00190     if (log.isActive(level)) {
00191       cout << log.formatMessage(level, "");
00192       return cout;
00193     } else {
00194       return *(log._nostream);
00195     }
00196   }
00197 
00198 }