Log2.h
Go to the documentation of this file.
00001 /*
00002  * Log class with levels.
00003  * Wouter Caarls <w.caarls@tudelft.nl>
00004  *
00005  * Basic usage: define a member variable
00006  *
00007  * class Class {
00008  *   CLog2 mLog;
00009  * };
00010  *
00011  * and initialize it in the constructor
00012  *
00013  *  Class() : mLog("group") { }
00014  *
00015  * where group is the name of the logging group (with default header [group]).
00016  * Then in any member function just use
00017  *
00018  *   mLogInfoLn("This is a log message");
00019  *
00020  * The usual options (console/file output, header text and colors) can be set with
00021  *
00022  *   mLog.setHeaderText("[MyFancyHeader] ");
00023  *
00024  * All these options will carry over to all log classes with the same group name.
00025  * The logging level is set with
00026  *
00027  *   mLog.setLevel(llCrawl);
00028  *
00029  * The logging level of all groups can be set at once with
00030  *
00031  *   gLogFactory->setLevel(llDebug);
00032  *
00033  * IMPORTANT NOTICE: For now, these classes are not thread-safe.
00034  * However, StdLog.h ensures that log lines are not interrupted.
00035  */
00036 
00037 #ifndef __LOG2_H_INCLUDED
00038 #define __LOG2_H_INCLUDED
00039 
00040 #include <map>
00041 #include <vector>
00042 
00043 #include "Log.h"
00044 
00045 #define STRINGIFY(x) #x
00046 #define TOSTRING(x) STRINGIFY(x)
00047 #define AT __FILE__ ":" TOSTRING(__LINE__)
00048 
00049 #define LOG2(obj, str, level) do {if ((level) >= (obj).getLevel()) { CCriticalSection logsection((obj).getStream()); (obj)(level) << str;}} while (0)
00050 
00051 #ifdef DEBUG
00052   #define logCrawl(obj, str)    LOG2(obj, str, llCrawl)
00053 #else
00054   #define logCrawl(obj, str)    do {} while (0)
00055 #endif
00056 
00057 #define logDebug(obj, str)      LOG2(obj, str, llDebug)
00058 #define logInfo(obj, str)       LOG2(obj, str, llInfo)
00059 #define logNotice(obj, str)     LOG2(obj, str, llNotice)
00060 #define logWarning(obj, str)    LOG2(obj, str, llWarning)
00061 #define logError(obj, str)      LOG2(obj, str, llError)
00062 #define logCritical(obj, str)   LOG2(obj, str, llCritical)
00063 #define logClean(obj, str)      LOG2(obj, str, llClean)
00064 
00065 #define logCrawlLn(obj, str)    logCrawl(obj, str << std::endl)
00066 #define logDebugLn(obj, str)    logDebug(obj, str << std::endl)
00067 #define logInfoLn(obj, str)     logInfo(obj, str << std::endl)
00068 #define logNoticeLn(obj, str)   logNotice(obj, str << std::endl)
00069 #define logWarningLn(obj, str)  logWarning(obj, str << std::endl)
00070 #define logErrorLn(obj, str)    logError(obj, str << std::endl)
00071 #define logCriticalLn(obj, str) logCritical(obj, str << std::endl)
00072 #define logCleanLn(obj, str)    logClean(obj, str << std::endl)
00073 
00074 #define mLogCrawl(str)          logCrawl(mLog, str)
00075 #define mLogDebug(str)          logDebug(mLog, str)
00076 #define mLogInfo(str)           logInfo(mLog, str)
00077 #define mLogNotice(str)         logNotice(mLog, str)
00078 #define mLogWarning(str)        logWarning(mLog, str)
00079 #define mLogError(str)          logError(mLog, str)
00080 #define mLogCritical(str)       logCritical(mLog, str)
00081 #define mLogClean(str)          logClean(mLog, str)
00082 
00083 #define mLogCrawlLn(str)        logCrawlLn(mLog, str)
00084 #define mLogDebugLn(str)        logDebugLn(mLog, str)
00085 #define mLogInfoLn(str)         logInfoLn(mLog, str)
00086 #define mLogNoticeLn(str)       logNoticeLn(mLog, str)
00087 #define mLogWarningLn(str)      logWarningLn(mLog, str)
00088 #define mLogErrorLn(str)        logErrorLn(mLog, str)
00089 #define mLogCriticalLn(str)     logCriticalLn(mLog, str)
00090 #define mLogCleanLn(str)            logCleanLn(mLog, str)
00091 #define mLogAssert(B)                   logAssertLnInternal(mLog, "Assertion failed at " AT  ": " #B, llError, B)
00092 
00093 // Standard header text delimiters
00094 #define LOG2HDRDELIMLEFT                "["
00095 #define LOG2HDRDELIMRIGHT               "] "
00096 #define LOG2OPENFILESSOFTLIMIT  100     // This is a soft limit. You can open more files, but this may lead to memory (re)allocation (unwanted in real-time apps).
00097 
00098 // Forward declaration
00099 class CLog2;
00100 
00101 class CLog2Factory
00102 {
00103         friend class CLog2;
00104   protected:
00105     ELogLevel   mLevel;
00106     bool                mTimeStamping;
00107     std::map<const std::string, CLogStream*>    mLogs;
00108     std::vector<FILE*>                                                  mOpenFiles;
00109 
00115     void equalizeHeaderTexts()
00116     {
00117         static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
00118         
00119         pthread_mutex_lock(&mutex);
00120     
00121         // Determine max header text length
00122         unsigned int maxWidth=0;
00123         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00124              it != mLogs.end(); ++it)
00125         {
00126                 if (it->second->headerText().length() > maxWidth)
00127                         maxWidth = it->second->headerText().length();
00128         }
00129 
00130         // Make all header text lengths equal. Skip the empty ones, they are empty for a reason!
00131         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00132              it != mLogs.end(); ++it)
00133         {
00134                 // First, pad with spaces
00135                 if (!it->second->headerText().empty())  // Skip empty headers
00136                 {
00137                                 it->second->headerText().resize(maxWidth, ' ');
00138                                 // Second, search if the standard limiter and delimiter are being used.
00139                                 std::string delimitStr(LOG2HDRDELIMRIGHT);
00140                                 std::string::size_type delimitLoc = it->second->headerText().rfind(delimitStr);
00141                                 // If found, move delimiter to the end
00142                                 if (delimitLoc != std::string::npos)
00143                                         it->second->headerText().erase(delimitLoc, delimitStr.length()).append(delimitStr);
00144                 }
00145         }
00146         
00147         pthread_mutex_unlock(&mutex);
00148     }
00149 
00150     // Use reportOpenFile() to report an opened FILE pointer that needs to be closed at a safe moment.
00151     // The logFactory closes the FILE pointers upon self destruction,
00152     // In real-time apps, this means after the RTDK lib (rt_printf) has cleaned up (do this at the end of main() using rt_print_cleanup()).
00153     void reportOpenFile(FILE* file, CLogStream& wrnReportLog)
00154     {
00155         if (file != NULL)
00156         {
00157                         mOpenFiles.push_back(file);
00158                         if (mOpenFiles.size() >= LOG2OPENFILESSOFTLIMIT)
00159                                 wrnReportLog << "[WARNING] Open file limit of log factory exceeded: memory allocation may occur. Try and increase LOG2OPENFILESSOFTLIMIT." << std::endl;
00160         }
00161     }
00162     void closeOpenFiles()
00163     {
00164         while (!mOpenFiles.empty())
00165         {
00166                 fclose(mOpenFiles.back());
00167                 mOpenFiles.pop_back();
00168         }
00169     }
00170 
00171   public:
00172           CLog2Factory() : mLevel(llInfo), mTimeStamping(false)
00173           {
00174                   mOpenFiles.reserve(LOG2OPENFILESSOFTLIMIT);
00175           }
00176           virtual ~CLog2Factory()
00177           {
00178                   // Close all reported open files upon destruction
00179                   closeOpenFiles();
00180                   // Cleanup the logs
00181                for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00182                      it != mLogs.end(); ++it)
00183                    delete it->second;
00184           }
00185 
00186     //virtual void setLevel(ELogLevel level) = 0;
00187     virtual void setLevel(ELogLevel level)
00188     {
00189       mLevel = level;
00190 
00191       for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00192            it != mLogs.end(); ++it)
00193         it->second->setLevel(mLevel);
00194     }
00195     virtual CLogStream &getLog(const std::string &name) = 0;
00196 
00197     virtual void enableConsoleOutput(bool bEnabled)
00198     {
00199       for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00200            it != mLogs.end(); ++it)
00201         it->second->enableConsoleOutput(bEnabled);
00202     }
00203     virtual void enableFileOutput(bool bEnabled, const std::string& filename="")
00204     {
00205         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00206             it != mLogs.end(); ++it)
00207          reportOpenFile(it->second->enableFileOutput(bEnabled, filename), *(it->second));
00208     }
00209     virtual void enableTimeStamping(bool bEnabled)
00210     {
00211         mTimeStamping = bEnabled;
00212 
00213         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00214             it != mLogs.end(); ++it)
00215          it->second->enableTimeStamping(bEnabled);
00216     }
00217     virtual void flushFileOutput()
00218     {
00219         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00220             it != mLogs.end(); ++it)
00221         it->second->flushFileOutput();
00222     }
00223     virtual void redirectConsoleOutput(FILE* file)
00224     {
00225         for (std::map<const std::string, CLogStream*>::iterator it = mLogs.begin();
00226             it != mLogs.end(); ++it)
00227         it->second->redirectConsoleOutput(file);
00228     }
00229     virtual ELogLevel getLevelFromString(const std::string &level)
00230     {
00231       if (!level.compare(0, 3, "cra")) return llCrawl;
00232       if (!level.compare(0, 1, "d"))   return llDebug;
00233       if (!level.compare(0, 1, "i"))   return llInfo;
00234       if (!level.compare(0, 1, "n"))   return llNotice;
00235       if (!level.compare(0, 1, "w"))   return llWarning;
00236       if (!level.compare(0, 1, "e"))   return llError;
00237       if (!level.compare(0, 3, "cri")) return llCritical;
00238       return llInfo;
00239     }
00240 };
00241 
00242 // To prevent a static initialization disaster when using global CLog2 objects,
00243 // we define gLogFactory as gLogFactory() returning a reference.
00244 CLog2Factory&   gLogFactory();
00245 
00246 class CLog2
00247 {
00248   private:
00249     CLogStream &mStream;
00250     std::string mName;
00251 
00252   public:
00253     CLog2(const std::string &name) :
00254         mStream(gLogFactory().getLog(name)),
00255         mName(name)
00256         { }
00257     CLog2& operator=(const CLog2 &obj) { return *this; }
00258 
00259     CLogStream &operator()(ELogLevel level)
00260     {
00261       switch (level)
00262       {
00263         case llCrawl:
00264           mStream.setMessageColor(FOREGROUND_GREEN);
00265           mStream.setSystemHeader("CRL: ");
00266           break;
00267         case llDebug:
00268           mStream.setMessageColor(FOREGROUND_GREEN);
00269           mStream.setSystemHeader("DBG: ");
00270           break;
00271         case llInfo:
00272           mStream.setMessageColor(FOREGROUND_GRAY);
00273           mStream.setSystemHeader("INF: ");
00274           break;
00275         case llNotice:
00276           mStream.setMessageColor(FOREGROUND_BLUE);
00277           mStream.setSystemHeader("NTC: ");
00278           break;
00279         case llWarning:
00280           mStream.setMessageColor(FOREGROUND_BROWN);
00281           mStream.setSystemHeader("WRN: ");
00282           break;
00283         case llError:
00284           mStream.setMessageColor(FOREGROUND_RED);
00285           mStream.setSystemHeader("ERR: ");
00286           break;
00287         case llCritical:
00288           mStream.setMessageColor(FOREGROUND_MAGENTA);
00289           mStream.setSystemHeader("CRT: ");
00290           break;
00291         case llClean:
00292           // Keep it clean!
00293           mStream.setSystemHeader("");
00294           break;
00295       }
00296 
00297       return mStream;
00298     }
00299 
00300     const std::string& name()                           { return mName; }
00301 
00302     inline ELogLevel getLevel() const       { return mStream.getLevel(); }
00303     void setLevel(ELogLevel level)          { mStream.setLevel(level); }
00304     void enableTimeStamping(bool bEnabled)      { mStream.enableTimeStamping(bEnabled); }
00305     void setHeaderText(const std::string &text)    { CCriticalSection setheader(mStream); mStream.setHeaderText(text); }
00306     void setHeaderColor(int color)          { mStream.setHeaderColor(color); }
00307     void enableConsoleOutput(bool bEnabled) { mStream.enableConsoleOutput(bEnabled); }
00308     void enableFileOutput(bool bEnabled, const std::string& filename="") { gLogFactory().reportOpenFile(mStream.enableFileOutput(bEnabled, filename), (*this)(llWarning)); }
00309     void flushFileOutput()                  { mStream.flushFileOutput(); }
00310     CLogStream &getStream()                        { return mStream; }
00311 };
00312 
00313 inline bool logAssertLnInternal(CLog2& log, const char *msg, ELogLevel level, bool condition)
00314 {
00315         if (!condition)
00316                 LOG2(log, msg << std::endl, level);
00317         return condition;
00318 }
00319 
00320 #endif /* __LOG2_H_INCLUDED */


threemxl
Author(s):
autogenerated on Fri Aug 28 2015 13:21:08