From 1399770a6c81a78b94708f5f7b60434197ebe27e Mon Sep 17 00:00:00 2001 From: okaestne Date: Wed, 23 Sep 2020 20:26:44 +0200 Subject: [PATCH] namespace instead of uber class, small improvements --- Log.cpp | 237 +++++++++++++++++++++++++++++++------------------------- Log.h | 162 ++++++++++++-------------------------- 2 files changed, 181 insertions(+), 218 deletions(-) diff --git a/Log.cpp b/Log.cpp index 9f84977..04d64ef 100644 --- a/Log.cpp +++ b/Log.cpp @@ -1,49 +1,113 @@ #include "Log.h" +#include // date/time +#include // ofstream (logging to file) +#include // std::put_time +#include // std::ostream, std::cout, std::cin +#include // list of outputs + +namespace Log { + /* - * class Log + * abstract class Output + * default implementations */ -// static members -std::vector Log::outputs; +// abstract base class for a log sink +class Output { +public: + Output() {} + Output(Level lvl_max) : lvl_max(lvl_max) {} + virtual ~Output() {} -Log::LeveledSink Log::fatal(Log::Level::FATAL); -Log::LeveledSink Log::error(Log::Level::ERROR); -Log::LeveledSink Log::warn(Log::Level::WARN); -Log::LeveledSink Log::note(Log::Level::NOTE); -Log::LeveledSink Log::info(Log::Level::INFO); -Log::LeveledSink Log::debug(Log::Level::DEBUG); -Log::LeveledSink Log::trace(Log::Level::TRACE); + virtual void log(Level lvl, std::stringbuf* sbuf) { + std::ostream* os = getOs(lvl); + if (os) { + *os << sbuf << std::endl; + } + }; -void Log::init() { - // add default console logger - if (outputs.empty()) - outputs.push_back(new Log::ConsoleOutput()); -} + virtual void setLogLevel(Level lvl) { lvl_max = lvl; } -void Log::stop() { - for (auto output : outputs) - delete output; - outputs.clear(); -} +protected: + Level lvl_max = INFO; + // returns the correct ostream for the given log-level + // or returns nullptr if no ostream is set/enabled for this level + virtual std::ostream* getOs(Level lvl) = 0; // abstract +}; -void Log::addLogfile(const std::string& filename, Level max) { - outputs.push_back(new Log::FileOutput(filename, max)); -} +// logging to stdout/stderr +class ConsoleOutput : public Output { +public: + ConsoleOutput() : Output() {} -void Log::addLogfile(const std::string& filename, Level min, Level max) { - outputs.push_back(new Log::FileOutput(filename, min, max)); -} + virtual bool setColoredOutput(bool enabled) { + // TODO: check the terminal's compatibility for colors + coloredOutput = enabled; + return true; + } -void Log::setConsoleLogLevel(Level lvl) { - outputs.at(0)->setLogLevel(lvl); // has to exist -} +private: + std::ostream* osStd = &std::cout; + std::ostream* osErr = &std::cerr; + bool coloredOutput; -void Log::setColoredOutput(bool enabled) { - ((Log::ConsoleOutput*) outputs.at(0))->setColoredOutput(enabled); // has to exist -} + virtual void log(Level lvl, std::stringbuf* sbuf) { + // OFF FATAL ERROR WARN NOTE INFO DEBUG TRACE + static const char* color_codes[] = {"", "1;31;40m", "31m", "33m", "96m", "32m", "0m", "0m"}; -void Log::log(Level lvl, std::stringbuf* strb) { + std::ostream* os = getOs(lvl); + + if (os) { + // print colors if enabled + if (coloredOutput) + *os << "\x1B[" << color_codes[lvl] << sbuf << "\x1B[0m"; + else + *os << sbuf; + + *os << std::endl; + } + } + + virtual std::ostream* getOs(Level lvl) { + // out of scope? + if (!lvl || lvl > lvl_max) + return nullptr; + + // stderr for FATAL, ERROR, WARN + if (lvl <= Level::WARN) + return osErr; + else + return osStd; + } +}; + +class FileOutput : public Output { +public: + FileOutput(const std::string& filename, Level lvl_max) + : Output(lvl_max), filename(filename), ofs(filename, std::ofstream::out | std::ofstream::app) {} + + FileOutput(const std::string& filename, Level lvl_min, Level lvl_max) + : Output(lvl_max), + filename(filename), + ofs(filename, std::ofstream::out | std::ofstream::app), + lvl_min(lvl_min) {} + +private: + std::string filename; + std::ofstream ofs; + Level lvl_min = FATAL; + + virtual std::ostream* getOs(Level lvl) { + if (lvl_min <= lvl && lvl <= lvl_max) + return &ofs; + return nullptr; + } +}; + +static std::vector outputs; + +void log(Level lvl, std::stringbuf* strb) { for (Output* out : outputs) { out->log(lvl, strb); // reset stringbuffer read pointer to the beginning @@ -52,27 +116,18 @@ void Log::log(Level lvl, std::stringbuf* strb) { } /* - * abstract class Output - * default implementations + * class Entry */ -Log::Output::Output() {} -Log::Output::Output(Log::Level lvl_max) : lvl_max(lvl_max) {} -Log::Output::~Output() {} - -void Log::Output::log(Log::Level lvl, std::stringbuf* sbuf) { - std::ostream* os = getOs(lvl); - if (os) { - printLogHeader(os, lvl); - *os << sbuf << std::endl; - } +Entry::Entry(Level lvl) : lvl(lvl) { + addMetadataHeader(); } -void Log::Output::setLogLevel(Log::Level lvl) { - lvl_max = lvl; +Entry::~Entry() { + log(lvl, ss.rdbuf()); } -void Log::Output::printLogHeader(std::ostream* os, Level lvl) { +void Entry::addMetadataHeader() { static const char* LevelTag[] = {"", "[FATAL] ", "[ERROR] ", "[WARN ] ", "[NOTE ] ", "[INFO ] ", "[DEBUG] ", "[TRACE] "}; @@ -80,72 +135,40 @@ void Log::Output::printLogHeader(std::ostream* os, Level lvl) { auto now = std::chrono::system_clock::now(); std::time_t now_c = std::chrono::system_clock::to_time_t(now); - *os << "[" << std::put_time(std::localtime(&now_c), "%F %T") << "]" << LevelTag[lvl]; + // datetime + ss << "[" << std::put_time(std::localtime(&now_c), "%F %T") << "]"; + // log level + ss << LevelTag[lvl]; } -/* - * class ConsoleOutput - */ - -Log::ConsoleOutput::ConsoleOutput() : Output() {} - -bool Log::ConsoleOutput::setColoredOutput(bool enabled) { - // TODO: check the terminals compatibility for colors - coloredOutput = enabled; - return true; +void init() { + // add default console logger + if (outputs.empty()) + outputs.push_back(new ConsoleOutput()); } -void Log::ConsoleOutput::log(Log::Level lvl, std::stringbuf* sbuf) { - static const char* esc_seq_start = "\033["; - static const char* esc_seq_reset = "\033[0m"; - // OFF FATAL ERROR WARN NOTE INFO DEBUG TRACE - static const char* color_codes[] = {"", "1;31;40m", "31m", "33m", "96m", "32m", "0m", "0m"}; - - std::ostream* os = getOs(lvl); - - if (os) { - // print colors if enabled - if (coloredOutput) - *os << esc_seq_start << color_codes[lvl]; - - Output::printLogHeader(os, lvl); - *os << sbuf; - - // reset color at end of the line - if (coloredOutput) - *os << esc_seq_reset; - - *os << std::endl; - } +void stop() { + for (auto output : outputs) + delete output; + outputs.clear(); } -std::ostream* Log::ConsoleOutput::getOs(Log::Level lvl) { - // out of scope? - if (!lvl || lvl > lvl_max) - return nullptr; - - // stderr for FATAL, ERROR, WARN - if (lvl <= Log::Level::WARN) - return osErr; - else - return osStd; +void addLogfile(const std::string& filename, Level max) { + outputs.push_back(new FileOutput(filename, max)); } -/* - * class FileOutput - * logging to file - */ - -Log::FileOutput::FileOutput(const std::string& filename, Log::Level lvl_max) - : Output(lvl_max), filename(filename), ofs(filename, std::ofstream::out | std::ofstream::app) {} - -Log::FileOutput::FileOutput(const std::string& filename, Log::Level lvl_min, Log::Level lvl_max) - : Output(lvl_max), filename(filename), ofs(filename, std::ofstream::out | std::ofstream::app), lvl_min(lvl_min) {} - -std::ostream* Log::FileOutput::getOs(Log::Level lvl) { - if (lvl_min <= lvl && lvl <= lvl_max) - return &ofs; - return nullptr; +void addLogfile(const std::string& filename, Level min, Level max) { + outputs.push_back(new FileOutput(filename, min, max)); } -Log::LeveledSink::LeveledSink(Log::Level level) : level(level) {} +void setConsoleLogLevel(Level lvl) { + outputs.at(0)->setLogLevel(lvl); // has to exist +} + +void setColoredOutput(bool enabled) { + ((ConsoleOutput*) outputs.at(0))->setColoredOutput(enabled); // has to exist +} + +LeveledSink::LeveledSink(Level level) : level(level) {} + +} // namespace Log \ No newline at end of file diff --git a/Log.h b/Log.h index b4666a5..6de7c98 100644 --- a/Log.h +++ b/Log.h @@ -1,123 +1,63 @@ #pragma once -#include // date/time -#include // ofstream (logging to file) -#include // std::put_time -#include // std::ostream, std::cout, std::cin -#include // std::stringstream (buffer for log entries) + +#include // std::stringstream (buffer for log entries) #include -#include // list of outputs -class Log { -public: - enum Level { OFF = 0, FATAL, ERROR, WARN, NOTE, INFO, DEBUG, TRACE }; +namespace Log { +enum Level { OFF = 0, FATAL, ERROR, WARN, NOTE, INFO, DEBUG, TRACE }; - // delete ctors as this class is used via static methods only - Log() = delete; - Log(const Log&) = delete; - Log& operator=(const Log&) = delete; - //~Log() = delete; +// set up the logger with a ConsoleOutput +void init(); +// close all output streams +void stop(); - // set up the logger with a ConsoleOutput - static void init(); - // close all output streams - static void stop(); +void addLogfile(const std::string& filename, Level max); +void addLogfile(const std::string& filename, Level min, Level max); +void setConsoleLogLevel(Level lvl); +void setColoredOutput(bool enabled); + +// Log entry that can be formed with various mixed data types +// by concatenation with the << operator +// Inspired from https://stackoverflow.com/a/8337882 +class Entry { private: - // abstract base class for a log sink - class Output { - public: - Output(); - Output(Log::Level lvl_max); - virtual ~Output(); - virtual void log(Log::Level lvl, std::stringbuf* sbuf); - virtual void setLogLevel(Log::Level lvl); + std::stringstream ss; + Level lvl; - protected: - Log::Level lvl_max = INFO; - // returns the correct ostream for the given log-level - // or returns nullptr if no ostream is set/enabled for this level - virtual std::ostream* getOs(Log::Level lvl) = 0; // abstract - virtual void printLogHeader(std::ostream* os, Log::Level lvl); - }; - - // logging to stdout/stderr - class ConsoleOutput : public Output { - public: - ConsoleOutput(); - virtual bool setColoredOutput(bool enabled); - - private: - std::ostream* osStd = &std::cout; - std::ostream* osErr = &std::cerr; - bool coloredOutput; - virtual void log(Log::Level lvl, std::stringbuf* sbuf); - virtual std::ostream* getOs(Log::Level lvl); - }; - - class FileOutput : public Output { - public: - FileOutput(const std::string& filename, Log::Level lvl_max); - FileOutput(const std::string& filename, Log::Level lvl_min, Log::Level lvl_max); - - private: - std::string filename; - std::ofstream ofs; - Log::Level lvl_min = FATAL; - virtual std::ostream* getOs(Log::Level lvl); - }; - - static std::vector outputs; + void addMetadataHeader(); public: - static void addLogfile(const std::string& filename, Level max); - static void addLogfile(const std::string& filename, Level min, Level max); + Entry(Level lvl); + Entry(Entry&&) = default; + ~Entry(); - static void setConsoleLogLevel(Level lvl); - static void setColoredOutput(bool enabled); - - // Log entry that can be formed with various mixed data types - // by concatenation with the << operator - // Inspired from https://stackoverflow.com/a/8337882 - class Entry { - private: - std::stringstream ss; - Log::Level lvl; - - public: - Entry(Log::Level lvl) : lvl(lvl) {} - Entry(Entry&&) = default; - ~Entry() { Log::log(lvl, ss.rdbuf()); } - - template - Entry& operator<<(const T& msg) { - ss << msg; - return *this; - } - }; - - class LeveledSink { - private: - Log::Level level; - public: - LeveledSink(Log::Level level); - - template - Log::Entry operator<<(const T& msg) { - Log::Entry entry(level); - entry << msg; - return entry; - } - }; - - // LeveledSinks - static LeveledSink fatal; - static LeveledSink error; - static LeveledSink warn; - static LeveledSink note; - static LeveledSink info; - static LeveledSink debug; - static LeveledSink trace; - -private: - static void log(Level lvl, std::stringbuf* strb); + template Entry& operator<<(const T& msg) { + ss << msg; + return *this; + } }; + +class LeveledSink { +private: + Level level; + +public: + LeveledSink(Level level); + + template Entry operator<<(const T& msg) { + Entry entry(level); + entry << msg; + return entry; + } +}; + +// LeveledSinks +static LeveledSink fatal(Level::FATAL); +static LeveledSink error(Level::ERROR); +static LeveledSink warn(Level::WARN); +static LeveledSink note(Level::NOTE); +static LeveledSink info(Level::INFO); +static LeveledSink debug(Level::DEBUG); +static LeveledSink trace(Level::TRACE); +} // namespace Log