logstream.hpp

Go to the documentation of this file.
00001 #pragma ident "$Id: logstream.hpp 2293 2010-02-12 18:14:16Z btolman $"
00002 
00008 #ifndef LOGSTREAMINCLUDE
00009 #define LOGSTREAMINCLUDE
00010 
00011 #include <sstream>
00012 #include <string>
00013 #include <iostream>
00014 
00018 enum LogLevel {ERROR, WARNING, INFO, VERBOSE, DEBUG,
00019                DEBUG1, DEBUG2, DEBUG3, DEBUG4, DEBUG5, DEBUG6, DEBUG7 };
00021 #ifndef FILELOG_MAX_LEVEL
00022 #define FILELOG_MAX_LEVEL DEBUG7
00023 #endif
00024 
00027 template <class T> class Log
00028 {
00029 public:
00030    Log() {};
00031    virtual ~Log();
00033    std::ostringstream& Put(LogLevel level = INFO);
00034 
00036    static bool& ReportTimeTags();
00038    static bool& ReportLevels();
00040    static LogLevel& ReportingLevel();
00042    static std::string ToString(LogLevel level);
00044    static LogLevel FromString(const std::string& level);
00045 
00046 protected:
00048    std::ostringstream os;
00049 
00050 #ifdef WIN32                  // see kludge note below
00051    static LogLevel reportingLevel;  
00052    static bool dumpTimeTags;        
00053    static bool dumpLevels;          
00054 #endif
00055 
00056 private:
00057    Log(const Log&);                 
00058    Log& operator=(const Log&);      
00059    std::string NowTime(void);       
00060 };
00061 
00062 template <class T> std::ostringstream& Log<T>::Put(LogLevel level)
00063 {
00064    if(Log<T>::ReportTimeTags()) os << NowTime() << " ";
00065    if(Log<T>::ReportLevels()) {
00066       os << ToString(level) << ": ";
00067       // add indentation for deep debug levels
00068       if(level > DEBUG) os << std::string(2*(level-DEBUG),' ');
00069    }
00070    return os;
00071 }
00072 
00073 template <class T> Log<T>::~Log()
00074 {
00075    os << std::endl;           // TD make optional?
00076    T::Output(os.str());
00077 }
00078 
00079 template <class T> bool& Log<T>::ReportLevels()
00080 {
00081 #ifndef WIN32                 // see kludge note below
00082    static bool dumpLevels = false;
00083 #endif
00084    return dumpLevels;
00085 }
00086 
00087 template <class T> bool& Log<T>::ReportTimeTags()
00088 {
00089 #ifndef WIN32                 // see kludge note below
00090    static bool dumpTimeTags = false;
00091 #endif
00092    return dumpTimeTags;
00093 }
00094 
00095 template <class T> LogLevel& Log<T>::ReportingLevel()
00096 {
00097 #ifndef WIN32                 // see kludge note below
00098    static LogLevel reportingLevel = INFO; // FILELOG_MAX_LEVEL;
00099 #endif
00100    return reportingLevel;
00101 }
00102 
00103 template <class T> std::string Log<T>::ToString(LogLevel level)
00104 {
00105    // note enum LogLevel above
00106         static const char* const buffer[] = {"ERROR", "WARNING", "INFO", "VERBOSE",
00107       "DEBUG","DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5", "DEBUG6", "DEBUG7" };
00108    return buffer[level];
00109 }
00110 
00111 template <class T> LogLevel Log<T>::FromString(const std::string& level)
00112 {
00113    // note enum LogLevel above
00114    if(level == "DEBUG7")  return DEBUG7;
00115    if(level == "DEBUG6")  return DEBUG6;
00116    if(level == "DEBUG5")  return DEBUG5;
00117    if(level == "DEBUG4")  return DEBUG4;
00118    if(level == "DEBUG3")  return DEBUG3;
00119    if(level == "DEBUG2")  return DEBUG2;
00120    if(level == "DEBUG1")  return DEBUG1;
00121    if(level == "DEBUG")   return DEBUG;
00122    if(level == "VERBOSE") return VERBOSE;
00123    if(level == "INFO")    return INFO;
00124    if(level == "WARNING") return WARNING;
00125    if(level == "ERROR")   return ERROR;
00126    Log<T>().Put(WARNING)
00127        << "Unknown logging level '" << level << "'. Using INFO level instead.";
00128    return INFO;
00129 }
00130 
00131 // time tag - platform dependent -----------------------------------------
00132 //#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
00133 #ifdef WIN32
00134 #include <sys/timeb.h>
00135 template <class T> inline std::string Log<T>::NowTime()
00136 {
00137    _timeb t;
00138    _ftime(&t);
00139    long sec=t.time;
00140    double dt=double(sec)+t.millitm/1000.;
00141    sec -= long(dt/86400)*86400;
00142    dt = double(sec)+t.millitm/1000.;
00143    char result[100] = {0};
00144    int h=int(dt/3600.);
00145    dt -= h*3600.;
00146    int m=int(dt/60.);
00147    dt -= m*60.;
00148    int s=int(dt);
00149    dt -= s;
00150    std::sprintf(result,"%02d:%02d:%02d.%03d",h,m,s,int(dt*1000.));
00151    return result;
00152 }
00153 
00154 #else    // not WIN32
00155 
00156 #include <sys/time.h>
00157 template <class T> inline std::string Log<T>::NowTime()
00158 {
00159    char buffer[11];
00160    time_t t;
00161    time(&t);
00162    tm r = {0};
00163    strftime(buffer, sizeof(buffer), "%X", localtime_r(&t, &r));
00164    struct timeval tv;
00165    gettimeofday(&tv, 0);
00166    char result[100] = {0};
00167    std::sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000); 
00168    return result;
00169 }
00170 
00171 #endif //WIN32
00172 
00173 // ------- end class LOG
00174 
00235 class ConfigureLOGstream
00236 {
00237 public:
00243    static std::ostream*& Stream();
00244 
00246    static void Output(const std::string& msg);
00247 };
00248 
00249 inline std::ostream*& ConfigureLOGstream::Stream()
00250 {
00251    static std::ostream *pStream = &(std::cout);
00252    return pStream;
00253 }
00254 
00255 inline void ConfigureLOGstream::Output(const std::string& msg)
00256 {   
00257    std::ostream *pStream = Stream();
00258    if(!pStream) return;
00259    *pStream << msg << std::flush;
00260 }
00261 
00262 //----- end class ConfigureLOGstream
00263 
00265 class ConfigureLOG : public Log<ConfigureLOGstream> {
00266 public:
00267    static std::ostream*& Stream()
00268    { return ConfigureLOGstream::Stream(); }
00269    static LogLevel Level(const std::string& str)
00270    { return FromString(str); }
00271 };
00272 
00273 //----- end class ConfigureLOG
00274 
00275 // This is a terrible kludge that nevertheless is necessary under Windows 2003 and
00276 // 2005 because global optimization (compiler switch /O1 or /O2 or /Og) causes the
00277 // reportingLevel to be defined ONLY in the module in which ReportingLevel() is
00278 // assigned. Rather than give up optimization or ReportingLevel(), I came up with
00279 // this kludge, which is to remove reportingLevel from the ReportingLevel() function
00280 // and make it a static member of the class, then initialize it outside the class.
00281 // Similarly for dumpTimeTags and dumpLevels.
00282 // Notice that this ought to fail (!) because putting this initialization in the
00283 // include file means that it occurs more than once - in every module in which it
00284 // appears. However Windows allows this; hence I have commented out the inner macro
00285 // here. Ah, the joys of developing under MS....
00286 #ifdef WIN32
00287 //#ifndef LOGSTREAM_INITIALIZE_REPORTING_LEVEL
00288 //#define LOGSTREAM_INITIALIZE_REPORTING_LEVEL
00289 template<> LogLevel Log<ConfigureLOGstream>::reportingLevel = INFO;
00290 template<> bool Log<ConfigureLOGstream>::dumpTimeTags = false;
00291 template<> bool Log<ConfigureLOGstream>::dumpLevels = false;
00292 //#endif
00293 #endif
00294 
00296 #define LOG(level) \
00297    if(level > FILELOG_MAX_LEVEL) ;\
00298    else if(level > ConfigureLOG::ReportingLevel() || !ConfigureLOGstream::Stream()) ;\
00299    else ConfigureLOG().Put(level)
00300 
00301 // conveniences
00302 #define pLOGstrm ConfigureLOGstream::Stream()
00303 #define LOGstrm *(ConfigureLOGstream::Stream())
00304 #define LOGlevel ConfigureLOG::ReportingLevel()
00305 //#define showLOGlevel ConfigureLOG::ReportLevels()
00306 //#define showLOGtime ConfigureLOG::ReportTimeTags()
00307 
00311 //#pragma optimize("g",off)      // turn off global optimization
00312 //#pragma optimize("",on)        // turn on optimizations turned on by compiler switch
00313 
00314 #endif //LOGSTREAMINCLUDE --- end of the include file. Test code follows in comments.
00315 
00316 /* Example:
00317 // testls.cpp
00318 #include "logstream.hpp"
00319 #include <fstream>
00320 
00321 void func(void);                   // testls0.cpp
00322 
00323 int main(int argc, char **argv)
00324 {
00325    try {
00326       std::ofstream oflog("testls.log",std::ios_base::out);
00327       ConfigureLOG::Stream() = &oflog;
00328       ConfigureLOG::ReportLevels() = true;
00329       ConfigureLOG::ReportTimeTags() = true;
00330       ConfigureLOG::ReportingLevel() = ConfigureLOG::Level("VERBOSE");
00331 
00332       LOG(INFO) << "In main(), level is "
00333          << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
00334       LOG(INFO) << "This is an INFO message in main";
00335       LOG(WARNING) << "This is a WARNING message in main";
00336       LOG(ERROR) << "This is an ERROR message in main";
00337 
00338       func();
00339       LOG(INFO) << "In main(), level is "
00340          << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
00341 
00342       LOG(INFO) << "Now write to testls.new.log";
00343       std::ofstream oflognew("testls.new.log",std::ios_base::out);
00344       ConfigureLOG::Stream() = &oflognew;
00345       ConfigureLOG::ReportTimeTags() = false;
00346       LOG(INFO) << "Change the level to DEBUG7 and turn off time tags";
00347       ConfigureLOG::ReportingLevel() = ConfigureLOG::FromString("DEBUG7");
00348 
00349       LOG(INFO) << "This is file testls.new.log";
00350       LOG(INFO) << "In main(), level is now "
00351          << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
00352       LOG(DEBUG) << "DEBUG message";
00353       LOG(DEBUG1) << "DEBUG1 message";
00354       LOG(DEBUG2) << "DEBUG2 message";
00355       LOG(DEBUG3) << "DEBUG3 message";
00356       LOG(DEBUG4) << "DEBUG4 message";
00357       LOG(DEBUG5) << "DEBUG5 message";
00358       LOG(DEBUG6) << "DEBUG6 message";
00359       LOG(DEBUG7) << "DEBUG7 message";
00360 
00361       LOG(INFO) << "Now go back to the original log file";
00362       ConfigureLOG::Stream() = &oflog;
00363 
00364       LOG(INFO) << "This is file testls.log again";
00365       LOG(INFO) << "In main(), level is now "
00366          << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
00367 
00368       LOG(INFO) << "Change the level to DEBUG3 and turn on time tags";
00369       ConfigureLOG::ReportTimeTags() = true;
00370       ConfigureLOG::ReportingLevel() = ConfigureLOG::FromString("DEBUG3");
00371       LOG(DEBUG) << "DEBUG message";
00372       LOG(DEBUG1) << "DEBUG1 message";
00373       LOG(DEBUG2) << "DEBUG2 message";
00374       LOG(DEBUG3) << "DEBUG3 message";
00375       LOG(DEBUG4) << "DEBUG4 message";
00376       LOG(DEBUG5) << "DEBUG5 message";
00377       LOG(DEBUG6) << "DEBUG6 message";
00378       LOG(DEBUG7) << "DEBUG7 message";
00379 
00380       return 0;
00381    }
00382    catch(std::exception& e) {
00383       std::cerr << "Exception: " << e.what() << std::endl;
00384    }
00385    catch(...) {
00386       std::cerr << "Unknown exception" << std::endl;
00387    }
00388    return 1;
00389 }
00390 // testls0.cpp
00391 #include "logstream.hpp"
00392 
00393 void func(void)
00394 {
00395    LOG(INFO) << "Here in func(), level is "
00396          << ConfigureLOG::ToString(ConfigureLOG::ReportingLevel());
00397    LOG(DEBUG) << "This is a debug statement inside func()";
00398 
00399    LOG(INFO) << "Change the level to DEBUG7";
00400    ConfigureLOG::ReportingLevel() = ConfigureLOG::FromString("DEBUG7");
00401    LOG(DEBUG5) << "DEBUG5 message";
00402    LOG(DEBUG6) << "DEBUG6 message";
00403    LOG(DEBUG7) << "DEBUG7 message";
00404    LOG(INFO) << "Change the level to DEBUG";
00405    ConfigureLOG::ReportingLevel() = ConfigureLOG::FromString("DEBUG");
00406    LOG(INFO) << "Leaving func()";
00407 }
00408 // execution:
00409 C:>testls
00410 
00411 C:>cat testls.log
00412 20:56:30.353 INFO: In main(), level is VERBOSE
00413 20:56:30.353 INFO: This is an INFO message in main
00414 20:56:30.353 WARNING: This is a WARNING message in main
00415 20:56:30.353 ERROR: This is an ERROR message in main
00416 20:56:30.353 INFO: Here in func(), level is VERBOSE
00417 20:56:30.353 INFO: Change the level to DEBUG7
00418 20:56:30.353 DEBUG5:           DEBUG5 message
00419 20:56:30.353 DEBUG6:             DEBUG6 message
00420 20:56:30.353 DEBUG7:               DEBUG7 message
00421 20:56:30.353 INFO: Change the level to DEBUG
00422 20:56:30.353 INFO: Leaving func()
00423 20:56:30.353 INFO: In main(), level is DEBUG
00424 20:56:30.353 INFO: Now write to testls.new.log
00425 INFO: This is file testls.log again
00426 INFO: In main(), level is now DEBUG7
00427 INFO: Change the level to DEBUG3 and turn on time tags
00428 20:56:30.353 DEBUG: DEBUG message
00429 20:56:30.353 DEBUG1:   DEBUG1 message
00430 20:56:30.353 DEBUG2:     DEBUG2 message
00431 20:56:30.353 DEBUG3:       DEBUG3 message
00432 
00433 C:>cat testls.new.log
00434 INFO: Change the level to DEBUG7 and turn off time tags
00435 INFO: This is file testls.new.log
00436 INFO: In main(), level is now DEBUG7
00437 DEBUG: DEBUG message
00438 DEBUG1:   DEBUG1 message
00439 DEBUG2:     DEBUG2 message
00440 DEBUG3:       DEBUG3 message
00441 DEBUG4:         DEBUG4 message
00442 DEBUG5:           DEBUG5 message
00443 DEBUG6:             DEBUG6 message
00444 DEBUG7:               DEBUG7 message
00445 INFO: Now go back to the original log file
00446 */

Generated on Wed May 23 03:30:59 2012 for GPS ToolKit Software Library by  doxygen 1.3.9.1