diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 0a4e905..60345a6 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,150 +1,159 @@ /******************************************************************************* * * File: log.h * * Contents: Facility for logging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include +// NOTE: One call for the various logging macros below is supposed to be used +// for registering one log entry. That goes natural with the non-stream +// implementations, which accept one string as argument. A more flexible way +// for printing log entries, for example colorizing text, is to use macros +// providing a log stream. It is important to note, however, that the stream +// obtained from one macro evaluation fits for printing one log entry, without +// nested/overlapping log entry emissions and the entry being closed with a +// newline. Should this simple recommendation not being followed, the result +// becomes hard to read due to missing line breaks and overlapping entries. +// FIXME: Thread-safety is another issue, which need to be addressed for proper +// logging. + /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { // Type-safe definition of log levels, use this in code. // NOTE: Keep values in sync with the corresponding preprocessor definitions // below. enum class LogLevel { Error, Warning, Info, Debug, Trace, // Number of log levels: NumLogLevels }; // Converts a LogLevel to its string representation. // PRE: logLevel != LogLevel::NumLogLevels std::string logLevelToString(const LogLevel logLevel); // Prints colorized tag for the given LogLevel. // PRE: logLevel != LogLevel::NumLogLevels std::ostream &operator<<(std::ostream &os, const LogLevel logLevel); } // End namespace rosa // Valid log levels, only for preprocessor definitions below. // NOTE: Keep in sync with the values of enum class rosa::LogLevel. #define ROSA_LOG_LEVEL_ERROR 0 #define ROSA_LOG_LEVEL_WARNING 1 #define ROSA_LOG_LEVEL_INFO 2 #define ROSA_LOG_LEVEL_DEBUG 3 #define ROSA_LOG_LEVEL_TRACE 4 /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ // Stream to print logs to. // FIXME: Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog // Simple logging implementation printing a string message. -// FIXME: Make logging thread-safe. #define ROSA_LOG_IMPL(level, output) \ do { \ - ROSA_LOG_OSTREAM << level << " " << __func__ << "@" << __FILENAME__ << ":" \ - << __LINE__ << ": " << (output) << std::endl; \ + ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ + << ":" << __LINE__ << ": " << (output) << std::endl; \ } while (false) // Simple logging implementation providing a stream to print to. -// FIXME: Make logging thread-safe. #define ROSA_LOG_STREAM_IMPL(level) \ - do { \ - ROSA_LOG_OSTREAM << level << " " << __func__ << "@" << __FILENAME__ << ":" \ - << __LINE__ << ": "; \ - } while (false); \ - ROSA_LOG_OSTREAM + ([](void) -> std::ostream & { \ + return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ + << __FILENAME__ << ":" << __LINE__ << ": "; \ + }()) namespace rosa { // Dummy ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa // A stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) #endif #endif // ROSA_SUPPORT_LOG_H