logger.hpp

Go to the documentation of this file.
00001 /*
00002 This file is part of GraphLab.
00003 
00004 GraphLab is free software: you can redistribute it and/or modify
00005 it under the terms of the GNU Lesser General Public License as 
00006 published by the Free Software Foundation, either version 3 of 
00007 the License, or (at your option) any later version.
00008 
00009 GraphLab is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU Lesser General Public License for more details.
00013 
00014 You should have received a copy of the GNU Lesser General Public 
00015 License along with GraphLab.  If not, see <http://www.gnu.org/licenses/>.
00016 */
00017 
00018 /**
00019  * @file logger.hpp
00020  * Usage:
00021  * First include logger.hpp. To logger, use the logger() function
00022  * There are 2 output levels. A "soft" output level which is
00023  * set by calling global_logger.set_log_level(), as well as a "hard" output
00024  * level OUTPUTLEVEL which is set in the source code (logger.h).
00025  *
00026  * when you call "logger()" with a loglevel and if the loglevel is greater than
00027  * both of the output levels, the string will be written.
00028  * written to a logger file. Otherwise, logger() has no effect.
00029  *
00030  * The difference between the hard level and the soft level is that the
00031  * soft level can be changed at runtime, while the hard level optimizes away
00032  * logging calls at compile time.
00033  *
00034  * @author Yucheng Low (ylow)
00035  */
00036 
00037 #ifndef GRAPHLAB_LOG_LOG_HPP
00038 #define GRAPHLAB_LOG_LOG_HPP
00039 #include <fstream>
00040 #include <sstream>
00041 #include <cstdlib>
00042 #include <cassert>
00043 #include <cstring>
00044 #include <cstdarg>
00045 #include <pthread.h>
00046 /**
00047  * \def LOG_FATAL
00048  *   Used for fatal and probably irrecoverable conditions
00049  * \def LOG_ERROR
00050  *   Used for errors which are recoverable within the scope of the function
00051  * \def LOG_WARNING
00052  *   Logs interesting conditions which are probably not fatal
00053  * \def LOG_INFO
00054  *   Used for providing general useful information
00055  * \def LOG_DEBUG
00056  *   Debugging purposes only
00057  */
00058 #define LOG_NONE 5
00059 #define LOG_FATAL 4
00060 #define LOG_ERROR 3
00061 #define LOG_WARNING 2
00062 #define LOG_INFO 1
00063 #define LOG_DEBUG 0
00064 
00065 /**
00066  * \def OUTPUTLEVEL
00067  *  The minimum level to logger at
00068  * \def LOG_NONE
00069  *  OUTPUTLEVEL to LOG_NONE to disable logging
00070  */
00071 
00072 #ifndef OUTPUTLEVEL
00073 #define OUTPUTLEVEL LOG_DEBUG
00074 #endif
00075 /// If set, logs to screen will be printed in color
00076 #define COLOROUTPUT
00077 
00078 
00079 /**
00080  * \def logger(lvl,fmt,...)
00081  *    extracts the filename, line number
00082  *     and function name and calls _log. It will be optimized
00083  *     away if LOG_NONE is set
00084  *     This relies on a few compiler macros. As far as I know, these
00085  *     macros are pretty standard among most other C++ compilers. 
00086  */
00087 #if OUTPUTLEVEL == LOG_NONE
00088 // totally disable logging
00089 #define logger(lvl,fmt,...)
00090 #define logbuf(lvl,fmt,...)
00091 #define logstream
00092 #else
00093 
00094 #define logger(lvl,fmt,...)                 \
00095     (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__,fmt,##__VA_ARGS__))
00096 
00097     
00098 #define logbuf(lvl,buf,len)                 \
00099     (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__,     \
00100                         __func__ ,__LINE__,buf,len))
00101 
00102 #define logstream(lvl)                      \
00103     (log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__) )
00104 #endif
00105 
00106 namespace logger_impl {
00107 struct streambuff_tls_entry {
00108   std::stringstream streambuffer;
00109   bool streamactive;
00110 };
00111 }
00112 
00113 
00114 /** Hack!  This is defined in assertions.cpp */
00115 void __print_back_trace();
00116 
00117 /**
00118   logging class.
00119   This writes to a file, and/or the system console.
00120 */
00121 class file_logger{
00122  public:
00123   /** Default constructor. By default, log_to_console is off,
00124       there is no logger file, and logger level is set to LOG_WARNING
00125   */
00126   file_logger();  
00127 
00128   ~file_logger();   /// destructor. flushes and closes the current logger file
00129 
00130   /** Closes the current logger file if one exists.
00131       if 'file' is not an empty string, it will be opened and 
00132       all subsequent logger output will be written into 'file'.
00133       Any existing content of 'file' will be cleared.
00134       Return true on success and false on failure.
00135   */
00136   bool set_log_file(std::string file);
00137 
00138   /// If consolelog is true, subsequent logger output will be written to stderr
00139   void set_log_to_console(bool consolelog) {
00140     log_to_console = consolelog;
00141   }
00142 
00143   /// Returns the current logger file.
00144   std::string get_log_file(void) {
00145     return log_file;
00146   }
00147 
00148   /// Returns true if output is being written to stderr
00149   bool get_log_to_console() {
00150     return log_to_console;
00151   }
00152 
00153   /// Returns the current logger level
00154   int get_log_level() {
00155     return log_level;
00156   }
00157 
00158   file_logger& start_stream(int lineloglevel,const char* file,const char* function, int line);
00159   
00160   template <typename T>
00161   file_logger& operator<<(T a) {
00162     // get the stream buffer
00163     logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
00164                                           pthread_getspecific(streambuffkey));
00165     if (streambufentry != NULL) {
00166       std::stringstream& streambuffer = streambufentry->streambuffer;
00167       bool& streamactive = streambufentry->streamactive;
00168 
00169       if (streamactive) streambuffer << a;
00170     }
00171     return *this;
00172   }
00173 
00174   file_logger& operator<<(const char* a) {
00175     // get the stream buffer
00176     logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
00177                                           pthread_getspecific(streambuffkey));
00178     if (streambufentry != NULL) {
00179       std::stringstream& streambuffer = streambufentry->streambuffer;
00180       bool& streamactive = streambufentry->streamactive;
00181 
00182       if (streamactive) {
00183         streambuffer << a;
00184         if (a[strlen(a)-1] == '\n') {
00185           stream_flush();
00186         }
00187       }
00188     }
00189     return *this;
00190   }
00191 
00192   file_logger& operator<<(std::ostream& (*f)(std::ostream&)){
00193     // get the stream buffer
00194     logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
00195                                           pthread_getspecific(streambuffkey));
00196     if (streambufentry != NULL) {
00197       std::stringstream& streambuffer = streambufentry->streambuffer;
00198       bool& streamactive = streambufentry->streamactive;
00199 
00200       typedef std::ostream& (*endltype)(std::ostream&);
00201       if (streamactive) {
00202         if (endltype(f) == endltype(std::endl)) {
00203           streambuffer << "\n";
00204           stream_flush();
00205           if(streamloglevel == LOG_FATAL) {
00206             __print_back_trace();
00207             throw "log fatal";
00208             // exit(EXIT_FAILURE);
00209           }
00210         }
00211       }
00212     }
00213     return *this;
00214   }
00215 
00216 
00217 
00218   /** Sets the current logger level. All logging commands below the current
00219       logger level will not be written. */
00220   void set_log_level(int new_log_level) {
00221     log_level = new_log_level;
00222   }
00223 
00224   /**
00225   * logs the message if loglevel>=OUTPUTLEVEL
00226   * This function should not be used directly. Use logger()
00227   *
00228   * @param loglevel Type of message \see LOG_DEBUG LOG_INFO LOG_WARNING LOG_ERROR LOG_FATAL
00229   * @param file File where the logger call originated
00230   * @param function Function where the logger call originated
00231   * @param line Line number where the logger call originated
00232   * @param fmt printf format string
00233   * @param arg var args. The parameters that match the format string
00234   */
00235   void _log(int loglevel,const char* file,const char* function,
00236                 int line,const char* fmt, va_list arg );
00237 
00238 
00239   void _logbuf(int loglevel,const char* file,const char* function,
00240                 int line,  const char* buf, int len);
00241                 
00242   void _lograw(int loglevel, const char* buf, int len);
00243 
00244   void stream_flush() {
00245     // get the stream buffer
00246     logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
00247                                           pthread_getspecific(streambuffkey));
00248     if (streambufentry != NULL) {
00249       std::stringstream& streambuffer = streambufentry->streambuffer;
00250 
00251       streambuffer.flush();
00252       _lograw(streamloglevel,
00253               streambuffer.str().c_str(),
00254               (int)(streambuffer.str().length()));
00255       streambuffer.str("");
00256     }
00257   }
00258  private:
00259   std::ofstream fout;
00260   std::string log_file;
00261   
00262   pthread_key_t streambuffkey;
00263   
00264   int streamloglevel;
00265   pthread_mutex_t mut;
00266   
00267   bool log_to_console;
00268   int log_level;
00269 
00270 };
00271 
00272 
00273 file_logger& global_logger();
00274 
00275 /**
00276 Wrapper to generate 0 code if the output level is lower than the log level
00277 */
00278 template <bool dostuff>
00279 struct log_dispatch {};
00280 
00281 template <>
00282 struct log_dispatch<true> {
00283   inline static void exec(int loglevel,const char* file,const char* function,
00284                 int line,const char* fmt, ... ) {
00285   va_list argp;
00286    va_start(argp, fmt);
00287    global_logger()._log(loglevel, file, function, line, fmt, argp);
00288    va_end(argp);
00289   }
00290 };
00291 
00292 template <>
00293 struct log_dispatch<false> {
00294   inline static void exec(int loglevel,const char* file,const char* function,
00295                 int line,const char* fmt, ... ) {}
00296 };
00297 
00298 
00299 struct null_stream {
00300   template<typename T>
00301   inline null_stream operator<<(T t) { return null_stream(); }
00302   inline null_stream operator<<(const char* a) { return null_stream(); }
00303   inline null_stream operator<<(std::ostream& (*f)(std::ostream&)) { return null_stream(); }
00304 };
00305 
00306 
00307 template <bool dostuff>
00308 struct log_stream_dispatch {};
00309 
00310 template <>
00311 struct log_stream_dispatch<true> {
00312   inline static file_logger& exec(int lineloglevel,const char* file,const char* function, int line) {
00313     return global_logger().start_stream(lineloglevel, file, function, line);
00314   }
00315 };
00316 
00317 template <>
00318 struct log_stream_dispatch<false> {
00319   inline static null_stream exec(int lineloglevel,const char* file,const char* function, int line) {
00320     return null_stream();
00321   }
00322 };
00323 
00324 #include <graphlab/logger/assertions.hpp>
00325 
00326 #endif
00327