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) { }
00021 
00022 
00023   Log::Log(const string& name, int level)
00024     : _name(name), _level(level) { }
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     auto theLog = existingLogs.find(name);
00057     if (theLog == existingLogs.end()) {
00058       int level = INFO;
00059       // Try running through all parent classes to find an existing level
00060       string tmpname = name;
00061       bool triedAllParents = false;
00062       while (! triedAllParents) {
00063         // Is there a default level?
00064         if (defaultLevels.find(tmpname) != defaultLevels.end()) {
00065           level = defaultLevels.find(tmpname)->second;
00066           break;
00067         }
00068         // Is there already such a logger? (NB. tmpname != name in later iterations)
00069         if (existingLogs.find(tmpname) != existingLogs.end()) {
00070           level = existingLogs.find(tmpname)->second.getLevel();
00071           break;
00072         }
00073         // Crop the string back to the next parent level
00074         size_t lastDot = tmpname.find_last_of(".");
00075         if (lastDot != string::npos) {
00076           tmpname = tmpname.substr(0, lastDot);
00077         } else {
00078           triedAllParents = true;
00079         }
00080       }
00081       // for (LevelMap::const_iterator l = defaultLevels.begin(); l != defaultLevels.end(); ++l) {
00082       // 
00083       // }
00084 
00085       // emplace returns pair<iterator,bool>
00086       auto result = existingLogs.emplace(name, Log(name, level));
00087       theLog = result.first;
00088     }
00089     return theLog->second;
00090   }
00091 
00092 
00093   string Log::getLevelName(int level) {
00094     /// @todo Do the map::upper_limit thing to find nearest level...
00095     switch(level) {
00096     case TRACE:
00097       return "TRACE";
00098     case DEBUG:
00099       return "DEBUG";
00100     case INFO:
00101       return "INFO";
00102     case WARN:
00103       return "WARN";
00104     case ERROR:
00105       return "ERROR";
00106     default:
00107       return "";
00108     }
00109     //throw Error("Enum value was not a valid log level. How did that happen?");
00110   }
00111 
00112 
00113   string Log::getColorCode(int level) {
00114     if (!Log::useShellColors) return "";
00115     // If the codes haven't been initialized, do so now.
00116     if (Log::colorCodes.empty()) {
00117       // If stdout is a valid tty, try to use the appropriate codes.
00118       if (isatty(1)) {
00119         /// @todo Test for VT100 compliance?
00120         Log::colorCodes[TRACE] = "\033[0;36m";
00121         Log::colorCodes[DEBUG] = "\033[0;34m";
00122         Log::colorCodes[INFO]  = "\033[0;32m";
00123         Log::colorCodes[WARN]  = "\033[0;33m";
00124         Log::colorCodes[ERROR] = "\033[0;31m";
00125         Log::endColorCode      = "\033[0m";
00126       } else {
00127         Log::colorCodes[TRACE] = "";
00128         Log::colorCodes[DEBUG] = "";
00129         Log::colorCodes[INFO] = "";
00130         Log::colorCodes[WARN] = "";
00131         Log::colorCodes[ERROR] = "";
00132       }
00133     }
00134     // Return the appropriate code from the colour map.
00135     /// @todo Do the map::upper_limit thing to find nearest level...
00136     return colorCodes[level];
00137   }
00138 
00139 
00140   Log::Level Log::getLevelFromName(const string& level) {
00141     if (level == "TRACE") return TRACE;
00142     if (level == "DEBUG") return DEBUG;
00143     if (level == "INFO") return INFO;
00144     if (level == "WARN") return WARN;
00145     if (level == "ERROR") return ERROR;
00146     throw Error("Couldn't create a log level from string '" + level + "'");
00147   }
00148 
00149 
00150   string Log::formatMessage(int level, const string& message) {
00151     string out;
00152     if (Log::useShellColors) {
00153       out += getColorCode(level);
00154     }
00155 
00156     if (Log::showLoggerName) {
00157       out += getName();
00158       out += ": ";
00159     }
00160 
00161     if (Log::showLogLevel) {
00162       out += Log::getLevelName(level);
00163       out += " ";
00164     }
00165 
00166     if (Log::showTimestamp) {
00167       time_t rawtime;
00168       time(&rawtime);
00169       char* timestr = ctime(&rawtime);
00170       timestr[24] = ' ';
00171       out += timestr;
00172       out += " ";
00173     }
00174 
00175     if (Log::useShellColors) {
00176       out += endColorCode;
00177     }
00178 
00179     out += " ";
00180     out += message;
00181  
00182     return out;
00183   }
00184 
00185 
00186   void Log::log(int level, const string& message) {
00187     if (isActive(level)) {
00188       cout << formatMessage(level, message) << endl;
00189     }
00190   }
00191 
00192 
00193   ostream& operator<<(Log& log, int level) {
00194     if (log.isActive(level)) {
00195       cout << log.formatMessage(level, "");
00196       return cout;
00197     } else {
00198       static ostream devNull(nullptr);
00199       return devNull;
00200     }
00201   }
00202 
00203 }