MsgLogger/src/MsgFormatter.cpp

Go to the documentation of this file.
00001 //--------------------------------------------------------------------------
00002 // File and Version Information:
00003 //      $Id: MsgFormatter.cpp 4198 2012-07-24 17:03:36Z salnikov@SLAC.STANFORD.EDU $
00004 //
00005 // Description:
00006 //      Class MsgFormatter
00007 //
00008 // Environment:
00009 //      Software developed for the BaBar Detector at the SLAC B-Factory.
00010 //
00011 // Author List:
00012 //      Andy Salnikov
00013 //
00014 // Copyright Information:
00015 //      Copyright (C) 2005 SLAC
00016 //
00017 //------------------------------------------------------------------------
00018 
00019 //-----------------------
00020 // This Class's Header --
00021 //-----------------------
00022 #include "MsgLogger/MsgFormatter.h"
00023 
00024 //-------------
00025 // C Headers --
00026 //-------------
00027 #include <algorithm>
00028 extern "C" {
00029 #include <time.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <sys/types.h>
00034 #include <unistd.h>
00035 }
00036 
00037 //---------------
00038 // C++ Headers --
00039 //---------------
00040 
00041 //-------------------------------
00042 // Collaborating Class Headers --
00043 //-------------------------------
00044 #include "MsgLogger/MsgLogRecord.h"
00045 
00046 //-----------------------------------------------------------------------
00047 // Local Macros, Typedefs, Structures, Unions and Forward Declarations --
00048 //-----------------------------------------------------------------------
00049 
00050 namespace {
00051 
00052   using namespace MsgLogger ;
00053 
00054   // get current time and format it
00055   void formattedTime ( std::string fmt, std::ostream& out ) ;
00056 
00057   // default format string
00058   const std::string s_defFmt = "%(time) [%(LVL)] {%(logger)} %(file):%(line) - %(message)" ;
00059 
00060   // these format strings are set with addGlobalFormat
00061   std::string s_appFmtMap [MsgLogLevel::LAST_LEVEL+1] ;
00062 
00063   // this format strings are set from the environment
00064   std::string s_envFmtMap [MsgLogLevel::LAST_LEVEL+1] ;
00065 
00066   void initFmtMap() {
00067 
00068     static bool initDone = false ;
00069     if ( not initDone ) {
00070       initDone = true ;
00071 
00072       // variable MSGLOGFMT defines global format string
00073       if ( const char* env = getenv ( "MSGLOGFMT" ) ) {
00074         std::fill_n ( s_envFmtMap, MsgLogLevel::LAST_LEVEL+1, env ) ;
00075       }
00076 
00077       // variables MSGLOGFMT_DBG, MSGLOGFMT_TRC, etc. define level-specific
00078       // format strings
00079       for ( int i = 0 ; i < MsgLogLevel::LAST_LEVEL+1 ; ++ i ) {
00080         MsgLogLevel lvl(i) ;
00081         std::string envname = "MSGLOGFMT_" ;
00082         envname += lvl.level3() ;
00083         if ( const char* env = getenv ( envname.c_str() ) ) {
00084           s_envFmtMap[i] = env ;
00085         }
00086       }
00087 
00088     }
00089   }
00090 
00091 }
00092 
00093 namespace MsgLogger {
00094 
00095 //              ----------------------------------------
00096 //              -- Public Function Member Definitions --
00097 //              ----------------------------------------
00098 
00099 // Constructor
00100 MsgFormatter::MsgFormatter( const std::string& afmt, const std::string& timefmt )
00101   : _timefmt(timefmt)
00102 {
00103   // initialize global format map
00104   ::initFmtMap() ;
00105 
00106   if ( _timefmt.empty() ) {
00107     if ( const char* env = getenv ( "MSGLOGTIMEFMT" ) ) {
00108       _timefmt = env ;
00109     } else {
00110       _timefmt = "%Y-%m-%d %H:%M:%S.%f" ;
00111     }
00112   }
00113 }
00114 
00115 // Destructor
00116 MsgFormatter::~MsgFormatter()
00117 {
00118 }
00119 
00120 // set format for all formatters
00121 void
00122 MsgFormatter::addGlobalFormat ( const std::string& fmt )
00123 {
00124   std::fill_n ( ::s_appFmtMap, MsgLogLevel::LAST_LEVEL+1, fmt ) ;
00125 }
00126 
00127 void
00128 MsgFormatter::addGlobalFormat ( MsgLogLevel level, const std::string& fmt )
00129 {
00130   ::s_appFmtMap[level.code()] = fmt ;
00131 }
00132 
00133 // add level-specific format
00134 void
00135 MsgFormatter::addFormat ( MsgLogLevel level, const std::string& fmt )
00136 {
00137   _fmtMap[level.code()] = fmt ;
00138 }
00139 
00140 // format message to the output stream
00141 void
00142 MsgFormatter::format ( const MsgLogRecord& rec, std::ostream& out )
00143 {
00144   const std::string& fmt = getFormat( rec.level() );
00145 
00146   // read format and fill the stream
00147   for ( std::string::const_iterator i = fmt.begin() ; i != fmt.end() ; ++ i ) {
00148 
00149     if ( *i != '%' ) {
00150       out.put( *i ) ;
00151       continue ;
00152     }
00153 
00154     std::string::const_iterator j = i ;
00155     if ( ++j == fmt.end() ) {
00156       out.put( *i ) ;
00157       continue ;
00158     }
00159 
00160     // escaped percent
00161     if ( *j == '%' ) {
00162       out.put( '%' ) ;
00163       i = j ;
00164       continue ;
00165     }
00166 
00167     // should be opening paren after percent
00168     if ( *j != '(' ) {
00169       out.put( *i ) ;
00170       continue ;
00171     }
00172 
00173     // find closing paren
00174     j = std::find ( j, fmt.end(), ')' ) ;
00175     if ( j == fmt.end() ) {
00176       out.put( *i ) ;
00177       continue ;
00178     }
00179 
00180     // get the name between parens
00181     std::string name ( i+2, j ) ;
00182     bool known = true ;
00183     if ( name == "logger" ) {
00184       if ( rec.logger().empty() ) {
00185         out << "/root/" ;
00186       } else {
00187         out << rec.logger() ;
00188       }
00189     } else if ( name == "level" ) {
00190       out << rec.level() ;
00191     } else if ( name == "L" ) {
00192       out << rec.level().levelLetter() ;
00193     } else if ( name == "LVL" ) {
00194       out << rec.level().level3() ;
00195     } else if ( name == "message" ) {
00196       // hack - reset buffer state
00197       rec.msgbuf()->pubseekoff ( 0, std::ios_base::beg, std::ios_base::in ) ;
00198       out << rec.msgbuf() ;
00199     } else if ( name == "path" ) {
00200       const char* path = rec.fileName() ;
00201       out << ( path ? path : "<empty>" ) ;
00202     } else if ( name == "file" ) {
00203       const char* path = rec.fileName() ;
00204       if ( path ) {
00205         const char* p = strrchr ( path, '/' ) ;
00206         if ( ! p ) p = path ;
00207         out << p+1 ;
00208       } else {
00209         out << "<empty>" ;
00210       }
00211     } else if ( name == "line" ) {
00212       out << rec.lineNum() ;
00213     } else if ( name == "time" ) {
00214       formattedTime ( _timefmt, out ) ;
00215     } else if ( name == "pid" ) {
00216       out << (unsigned long)getpid() ;
00217     } else {
00218       known = false ;
00219     }
00220 
00221     // advance
00222     if ( known ) {
00223       i = j ;
00224     }
00225 
00226   }
00227 
00228 
00229 }
00230 
00231 // get a format string for a given level
00232 const std::string&
00233 MsgFormatter::getFormat ( MsgLogLevel level ) const
00234 {
00235   int lvl = level.code() ;
00236   if ( not _fmtMap[lvl].empty() ) {
00237     return _fmtMap[lvl] ;
00238   } else if ( not ::s_envFmtMap[lvl].empty() ) {
00239     return ::s_envFmtMap[lvl] ;
00240   } else if ( not ::s_appFmtMap[lvl].empty() ) {
00241     return ::s_appFmtMap[lvl] ;
00242   } else {
00243     return ::s_defFmt ;
00244   }
00245 }
00246 
00247 
00248 
00249 } // namespace MsgLogger
00250 
00251 namespace {
00252 
00253   // get current time and format it
00254   void formattedTime ( std::string fmt, std::ostream& out )
00255   {
00256     // get seconds/nanoseconds
00257     struct timespec ts;
00258     clock_gettime( CLOCK_REALTIME, &ts );
00259 
00260     // convert to break-down time
00261     struct tm tms ;
00262     localtime_r( &ts.tv_sec, &tms );
00263 
00264     // replace %f in the format string with miliseconds
00265     std::string::size_type n = fmt.find("%f") ;
00266     if ( n != std::string::npos ) {
00267       char subs[4] ;
00268       snprintf ( subs, 4, "%03d", int(ts.tv_nsec/1000000) ) ;
00269       while ( n != std::string::npos ) {
00270         fmt.replace ( n, 2, subs ) ;
00271         n = fmt.find("%f") ;
00272       }
00273     }
00274 
00275     char buf[1024] ;
00276     strftime(buf, 1024, fmt.c_str(), &tms );
00277     out << buf ;
00278 
00279   }
00280 
00281 }

Generated on 19 Dec 2016 for PSANAclasses by  doxygen 1.4.7