C++ Utils
0.1
|
00001 00037 #ifndef UTILS_LOGGER_H 00038 #define UTILS_LOGGER_H 00039 00040 #include "utils/timeutils.h" 00041 00042 #include <algorithm> 00043 #include <cstdlib> 00044 #include <ctime> 00045 #include <execinfo.h> 00046 #include <iostream> 00047 #include <sstream> 00048 #include <vector> 00049 00050 #ifndef LOG_LEVEL 00051 #ifdef NDEBUG 00052 #define LOG_LEVEL 2 00053 #else // NDEBUG 00054 #define LOG_LEVEL 3 00055 #endif // NDEBUG 00056 #endif // LOG_LEVEL 00057 00058 #ifndef LOG_PREFIX 00059 #define LOG_PREFIX "%a %b %d %X" 00060 #endif // DEBUG_PRFIX 00061 00062 #ifndef LOG_ABORT 00063 #ifdef MPI_VERSION 00064 #define LOG_ABORT MPI_Abort(MPI_COMM_WORLD, -1) 00065 #else // MPI_VERSION 00066 #define LOG_ABORT abort() 00067 #endif // MPI_VERSION 00068 #endif // LOG_ABORT 00069 00070 #ifndef BACKTRACE_SIZE 00071 #define BACKTRACE_SIZE 50 00072 #endif // BACKTRACE_SIZE 00073 00077 namespace utils 00078 { 00079 00085 class Logger 00086 { 00087 public: 00089 enum DebugType { 00091 DEBUG, 00093 INFO, 00095 WARNING, 00097 ERROR 00098 }; 00099 private: 00101 struct Stream { 00103 DebugType type; 00105 int rank; 00107 int ref; 00109 std::stringstream buffer; 00111 bool space; 00112 00116 Stream(DebugType t, int r) 00117 : type(t), rank(r), ref(1), 00118 buffer(std::stringstream::out), 00119 space(true) { } 00120 } *stream; 00124 public: 00132 Logger(DebugType t, int rank) 00133 : stream(new Stream(t, rank)) 00134 { 00135 stream->buffer << utils::TimeUtils::timeAsString(LOG_PREFIX); 00136 00137 switch (t) { 00138 case DEBUG: 00139 stream->buffer << ", Debug: "; 00140 break; 00141 case INFO: 00142 stream->buffer << ", Info: "; 00143 break; 00144 case WARNING: 00145 stream->buffer << ", Warn: "; 00146 break; 00147 case ERROR: 00148 stream->buffer << ", Error: "; 00149 break; 00150 } 00151 } 00155 Logger(const Logger& o) : stream(o.stream) { stream->ref++; }; 00156 ~Logger() 00157 { 00158 if (!--stream->ref) { 00159 if (stream->rank == 0) { 00160 if (stream->type == INFO) 00161 std::cout << stream->buffer.str() << std::endl; 00162 else 00163 std::cerr << stream->buffer.str() << std::endl; 00164 } 00165 00166 if (stream->type == ERROR) { 00167 delete stream; 00168 stream = 0L; // Avoid double free if LOG_ABORT does 00169 // does not exit the program 00170 00171 // Backtrace 00172 if (BACKTRACE_SIZE > 0) { 00173 void *buffer[BACKTRACE_SIZE]; 00174 int nptrs = backtrace(buffer, BACKTRACE_SIZE); 00175 char** strings = backtrace_symbols(buffer, nptrs); 00176 00177 // Buffer output to avoid interlacing with other processes 00178 std::stringstream outputBuffer; 00179 outputBuffer << "Backtrace:" << std::endl; 00180 for (int i = 0; i < nptrs; i++) 00181 outputBuffer << strings[i] << std::endl; 00182 free(strings); 00183 00184 // Write backtrace to stderr 00185 std::cerr << outputBuffer.str() << std::flush; 00186 } 00187 00188 LOG_ABORT; 00189 } 00190 00191 delete stream; 00192 } 00193 } 00194 00198 Logger &operator=(const Logger& other) 00199 { 00200 if (this != &other) { 00201 Logger copy(other); 00202 std::swap(stream, copy.stream); 00203 } 00204 return *this; 00205 } 00206 00207 00208 /********* Space handling *********/ 00209 00213 Logger &space() 00214 { 00215 stream->space = true; 00216 stream->buffer << ' '; 00217 return *this; 00218 } 00222 Logger &nospace() 00223 { 00224 stream->space = false; 00225 return *this; 00226 } 00230 Logger &maybeSpace() 00231 { 00232 if (stream->space) 00233 stream->buffer << ' '; 00234 return *this; 00235 } 00236 00240 template<typename T> 00241 Logger &operator<<(T t) 00242 { 00243 stream->buffer << t; 00244 return maybeSpace(); 00245 } 00246 00250 Logger &operator<<(const std::string& t) 00251 { 00252 stream->buffer << '"' << t << '"'; 00253 return maybeSpace(); 00254 } 00255 00259 Logger &operator<<(std::string& t) 00260 { 00261 stream->buffer << '"' << t << '"'; 00262 return maybeSpace(); 00263 } 00264 00268 Logger &operator<<(std::ostream& (*func)(std::ostream&)) 00269 { 00270 stream->buffer << func; 00271 return *this; // No space in this case 00272 } 00273 00277 Logger &operator<<(Logger& (*func)(Logger&)) 00278 { 00279 func(*this); 00280 return *this; 00281 } 00282 }; 00283 00292 inline Logger& space(Logger &logger) 00293 { 00294 return logger.space(); 00295 } 00296 00303 inline Logger& nospace(Logger &logger) 00304 { 00305 return logger.nospace(); 00306 } 00307 00311 class NoLogger 00312 { 00313 public: 00314 NoLogger() {}; 00315 ~NoLogger() {}; 00316 00320 template<typename T> 00321 NoLogger &operator<<(const T&) 00322 { 00323 return *this; 00324 } 00325 00329 NoLogger &operator<<(std::ostream& (*func)(std::ostream&)) 00330 { 00331 return *this; 00332 } 00333 00338 NoLogger &operator<<(Logger& (*func)(Logger&)) 00339 { 00340 return *this; 00341 } 00342 }; 00343 00349 template <typename T> 00350 inline Logger &operator<<(Logger debug, const std::vector<T> &list) 00351 { 00352 debug.nospace() << '('; 00353 for (size_t i = 0; i < list.size(); i++) { 00354 if (i) 00355 debug << ", "; 00356 debug << list[i]; 00357 } 00358 debug << ')'; 00359 00360 return debug.space(); 00361 } 00362 00363 } 00364 00365 // Define global functions 00366 00372 inline 00373 utils::Logger logError() 00374 { 00375 return utils::Logger(utils::Logger::ERROR, 0); 00376 } 00377 00378 #if LOG_LEVEL >= 1 00379 00384 inline 00385 utils::Logger logWarning( int rank = 0 ) 00386 { 00387 return utils::Logger(utils::Logger::WARNING, rank); 00388 } 00389 #else // LOG_LEVEL >= 1 00390 00395 inline 00396 utils::NoLogger logWarning( int = 0 ) { return utils::NoLogger(); } 00397 #endif // LOG_LEVEL >= 1 00398 00399 #if LOG_LEVEL >= 2 00400 00405 inline 00406 utils::Logger logInfo( int rank = 0 ) 00407 { 00408 return utils::Logger(utils::Logger::INFO, rank); 00409 } 00410 #else // LOG_LEVEL >= 2 00411 00416 inline 00417 utils::NoLogger logInfo( int = 0 ) { return utils::NoLogger(); } 00418 #endif // LOG_LEVEL >= 2 00419 00420 #if LOG_LEVEL >= 3 00421 00426 inline 00427 utils::Logger logDebug( int rank = 0 ) 00428 { 00429 return utils::Logger(utils::Logger::DEBUG, rank); 00430 } 00431 #else // LOG_LEVEL >= 3 00432 00437 inline 00438 utils::NoLogger logDebug( int = 0 ) { return utils::NoLogger(); } 00439 #endif // LOG_LEVEL >= 3 00440 00441 00442 // Use for variables unused when compiling with NDEBUG 00443 #ifdef NDEBUG 00444 #define NDBG_UNUSED(x) ((void) x) 00445 #else // NDEBUG 00446 #define NDBG_UNUSED(x) 00447 #endif // NDEBUG 00448 00449 #endif // UTILS_LOGGER_H