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       existingLogs[name] = new Log(name, level);
00083     }
00084     return *existingLogs[name];
00085   }
00086 
00087 
00088   string Log::getLevelName(int level) {
00089     /// @todo Do the map::upper_limit thing to find nearest level...
00090     switch(level) {
00091     case TRACE:
00092       return "TRACE";
00093     case DEBUG:
00094       return "DEBUG";
00095     case INFO:
00096       return "INFO";
00097     case WARN:
00098       return "WARN";
00099     case ERROR:
00100       return "ERROR";
00101     default:
00102       return "";
00103     }
00104     //throw Error("Enum value was not a valid log level. How did that happen?");
00105   }
00106 
00107 
00108   string Log::getColorCode(int level) {
00109     if (!Log::useShellColors) return "";
00110     // If the codes haven't been initialized, do so now.
00111     if (Log::colorCodes.empty()) {
00112       // If stdout is a valid tty, try to use the appropriate codes.
00113       if (isatty(1)) {
00114         /// @todo Test for VT100 compliance?
00115         Log::colorCodes[TRACE] = "\033[0;37m";
00116         Log::colorCodes[DEBUG] = "\033[0;36m";
00117         Log::colorCodes[INFO]  = "\033[0;32m";
00118         Log::colorCodes[WARN]  = "\033[0;33m";
00119         Log::colorCodes[ERROR] = "\033[0;31m";
00120         Log::endColorCode      = "\033[0m";
00121       } else {
00122         Log::colorCodes[TRACE] = "";
00123         Log::colorCodes[DEBUG] = "";
00124         Log::colorCodes[INFO] = "";
00125         Log::colorCodes[WARN] = "";
00126         Log::colorCodes[ERROR] = "";
00127       }
00128     }
00129     // Return the appropriate code from the colour map.
00130     /// @todo Do the map::upper_limit thing to find nearest level...
00131     return colorCodes[level];
00132   }
00133 
00134 
00135   Log::Level Log::getLevelFromName(const string& level) {
00136     if (level == "TRACE") return TRACE;
00137     if (level == "DEBUG") return DEBUG;
00138     if (level == "INFO") return INFO;
00139     if (level == "WARN") return WARN;
00140     if (level == "ERROR") return ERROR;
00141     throw Error("Couldn't create a log level from string '" + level + "'");
00142   }
00143 
00144 
00145   string Log::formatMessage(int level, const string& message) {
00146     string out;
00147     if (Log::useShellColors) {
00148       out += getColorCode(level);
00149     }
00150 
00151     if (Log::showLoggerName) {
00152       out += getName();
00153       out += ": ";
00154     }
00155 
00156     if (Log::showLogLevel) {
00157       out += Log::getLevelName(level);
00158       out += " ";
00159     }
00160 
00161     if (Log::showTimestamp) {
00162       time_t rawtime;
00163       time(&rawtime);
00164       char* timestr = ctime(&rawtime);
00165       timestr[24] = ' ';
00166       out += timestr;
00167       out += " ";
00168     }
00169 
00170     if (Log::useShellColors) {
00171       out += endColorCode;
00172     }
00173 
00174     out += " ";
00175     out += message;
00176  
00177     return out;
00178   }
00179 
00180 
00181   void Log::log(int level, const string& message) {
00182     if (isActive(level)) {
00183       cout << formatMessage(level, message) << endl;
00184     }
00185   }
00186 
00187 
00188   ostream& operator<<(Log& log, int level) {
00189     if (log.isActive(level)) {
00190       cout << log.formatMessage(level, "");
00191       return cout;
00192     } else {
00193       return *(log._nostream);
00194     }
00195   }
00196 
00197 }