diff --git a/examples/simple/simple.cpp b/examples/simple/simple.cpp index d4f6731..6dc00e8 100644 --- a/examples/simple/simple.cpp +++ b/examples/simple/simple.cpp @@ -1,25 +1,29 @@ /******************************************************************************* * * File: simple.cpp * * Contents: A very simple example to use RoSA Core library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/Unit.h" +#include "rosa/support/log.h" +#include "rosa/support/terminal_colors.h" #include using namespace rosa; +using namespace rosa::terminal; int main(void) { - std::cout << library_string() << " -- simple example" << std::endl; - Unit unit1, unit2("Second"), unit3; - std::cout << unit2 << std::endl; + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "simple example" << Color::Default << std::endl; + Unit Unit1, Unit2("Second"), Unit3; + LOG_TRACE_STREAM << Unit2 << std::endl; return 0; } diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index 47e94de..a0c1727 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,82 +1,86 @@ /******************************************************************************* * * File: debug.hpp * * Contents: Facility for debugging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/types.hpp" +#include "rosa/support/terminal_colors.h" #include #include namespace rosa { // Returns an output stream to use for debugging. std::ostream &dbgs(void); // Prints an array to the ostream. template std::ostream &operator<<(std::ostream &os, const std::array &arr) { os << '['; for (unsigned I = 0; I < size; ++I) { if (I) { os << ','; } os << (PRINTABLE(T)) arr[I]; } os << ']'; return os; } } // End namespace rosa #ifndef ROSA_ENABLE_ASSERTIONS #define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ - rosa::dbgs() << __FILENAME__ << ":" << __LINE__ \ - << ": requirement failed: '" << #stmt << "'" << std::endl; \ + rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ + << __LINE__ << ": requirement failed: '" << #stmt << "'" \ + << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ - rosa::dbgs() << __FILENAME__ << ":" << __LINE__ \ - << ": requirement failed: '" << #stmt << "'" << std::endl; \ + rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ + << __LINE__ << ": requirement failed: '" << #stmt << "'" \ + << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ - rosa::dbgs() << #V << " (" << __FILENAME__ << ":" << __LINE__ \ - << "): " << (V) << std::endl; \ + rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ + << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ + << std::endl; \ } while (false) #else // defined(NDEBUG) #define DEBUG(X) ROSA_IGNORE_UNUSED(X) #define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index e3bde38..7f1cce6 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,143 +1,148 @@ /******************************************************************************* * * 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 /* **************************************************************************** * Log Levels * * ****************************************************************************/ -// 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 - namespace rosa { -// Type-safe definition of log levels. -// NOTE: Keep values in sync with the corresponding preprocessor definitions. +// 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 = ROSA_LOG_LEVEL_ERROR, - Warning = ROSA_LOG_LEVEL_WARNING, - Info = ROSA_LOG_LEVEL_INFO, - Debug = ROSA_LOG_LEVEL_DEBUG, - Trace = ROSA_LOG_LEVEL_TRACE + Error, + Warning, + Info, + Debug, + Trace, + // Number of log levels: + NumLogLevels }; // Converts a LogLevel to its string representation. std::string logLevelToString(const LogLevel logLevel); +// Prints colorized tag for the given LogLevel. +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 << "[" << rosa::logLevelToString(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 << "[" << rosa::logLevelToString(level) << "] " \ - << __func__ << "@" << __FILENAME__ << ":" << __LINE__ \ - << ": "; \ + ROSA_LOG_OSTREAM << level << " " << __func__ << "@" << __FILENAME__ << ":" \ + << __LINE__ << ": "; \ } while (false); \ ROSA_LOG_OSTREAM 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::Error) +#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::Error) +#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 diff --git a/include/rosa/support/terminal_colors.h b/include/rosa/support/terminal_colors.h new file mode 100644 index 0000000..fee3b4e --- /dev/null +++ b/include/rosa/support/terminal_colors.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * + * File: terminal_colors.h + * + * Contents: Facility for printing colorized text to terminals supporting it. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_SUPPORT_TERMINAL_COLORS_H +#define ROSA_SUPPORT_TERMINAL_COLORS_H + +#include + +namespace rosa { +namespace terminal { + +// Text colors for colorizable terminals. +enum class Color { + Default, + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + Lightgrey, + Darkgrey, + Lightred, + Lightgreen, + Lightyellow, + Lightblue, + LightMagenta, + Lightcyan, + White, + // Number of Color values: + NumColors +}; + +// Operator handling Color commands sent to output streams. +std::ostream &operator<<(std::ostream &os, const Color color); + +} // End namespace terminal +} // End namespace rosa + +#endif // ROSA_SUPPORT_TERMINAL_COLORS_H + diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index 9d716be..410552a 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(ROSASupport debug.cpp + terminal_colors.cpp log.cpp ) diff --git a/lib/support/log.cpp b/lib/support/log.cpp index c0de18f..dcfe85c 100644 --- a/lib/support/log.cpp +++ b/lib/support/log.cpp @@ -1,31 +1,50 @@ /******************************************************************************* * * File: log.cpp * * Contents: Facility for logging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ +#include "rosa/support/debug.hpp" #include "rosa/support/log.h" +#include "rosa/support/terminal_colors.h" +#include namespace rosa { +// Textual representation of LogLevels. +constexpr std::array(LogLevel::NumLogLevels)> + LogLevelStrings{{"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"}}; + +// Terminal colors associated to LogLevels. +constexpr std::array(LogLevel::NumLogLevels)> + LogLevelColors{{ + terminal::Color::Red, // LogLevel::Error + terminal::Color::Yellow, // LogLevel::Warning + terminal::Color::Green, // LogLevel::Info + terminal::Color::Blue, // LogLevel::Debug + terminal::Color::Default, // LogLevel::Trace + }}; + std::string logLevelToString(const LogLevel logLevel) { - switch(logLevel) { - case LogLevel::Error: return "ERROR"; - case LogLevel::Warning: return "WARNING"; - case LogLevel::Info: return "INFO"; - case LogLevel::Debug: return "DEBUG"; - case LogLevel::Trace: return "TRACE"; - default: ROSA_CRITICAL("Invalid LogLevel"); - } + ASSERT(logLevel != LogLevel::NumLogLevels); + return LogLevelStrings[static_cast(logLevel)]; +} + +std::ostream &operator<<(std::ostream &os, const LogLevel logLevel) { + ASSERT(logLevel != LogLevel::NumLogLevels); + os << LogLevelColors[static_cast(logLevel)] << "[" + << logLevelToString(logLevel) << "]" << terminal::Color::Default; + return os; } std::ostream LogSink(nullptr); } // End namespace rosa diff --git a/lib/support/terminal_colors.cpp b/lib/support/terminal_colors.cpp new file mode 100644 index 0000000..9a4c84f --- /dev/null +++ b/lib/support/terminal_colors.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * + * File: terminal_colors.cpp + * + * Contents: Facility for printing colorized text to terminals supporting it. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/support/terminal_colors.h" +#include "rosa/support/debug.hpp" +#include "rosa/config/config.h" +#include + +namespace rosa { +namespace terminal { + +#ifdef ROSA_POSIX +// ANSI color codes for POSIX terminals. +// NOTE: Use std::array for runtime efficiency, though static casting is +// necessary for indexing. +constexpr std::array(Color::NumColors)> + ANSIColorCodes{{ + "0", // Default - Attribute reset + "30", // Black + "31", // Red + "32", // Green + "33", // Yelow + "34", // Blue + "35", // Magenta + "36", // Cyan + "37", // Lightgrey + "90", // Darkgrey + "91", // Lightred + "92", // Lightgreen + "93", // Lightyellow + "94", // Lightblue + "95", // Lightmagenta + "96", // Lightcyan + "97" // White + }}; +#endif // defined ROSA_POSIX + +std::ostream &operator<<(std::ostream &os, const Color color) { + ASSERT(color != Color::NumColors); + // Handle color only when printing to terminal and it supports colors. +#ifdef ROSA_POSIX + // FIXME: Make terminal identification more robust! What if rdbuf of a + // standard stream is changed in the software? + if (os.rdbuf() == std::cout.rdbuf() || os.rdbuf() == std::cerr.rdbuf() || + os.rdbuf() == std::clog.rdbuf()) { + os << "\033[" << ANSIColorCodes[static_cast(color)] << "m"; + } +#endif // defined ROSA_POSIX + return os; +} + +} // End namespace terminal +} // End namespace rosa +