#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 #include // list of outputs class Log { public: 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 static void init(); // close all output streams static void stop(); 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); 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; public: static void addLogfile(const std::string& filename, Level max); static void addLogfile(const std::string& filename, Level min, Level max); 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; } }; template static void fatal(const T& msg) { Entry(FATAL) << msg; } static Entry fatal() { return Entry(FATAL); } template static void error(const T& msg) { Entry(ERROR) << msg; } static Entry error() { return Entry(ERROR); } template static void warn(const T& msg) { Entry(WARN) << msg; } static Entry warn() { return Entry(WARN); } template static void note(const T& msg) { Entry(NOTE) << msg; } static Entry note() { return Entry(NOTE); } template static void info(const T& msg) { Entry(INFO) << msg; } static Entry info() { return Entry(INFO); } template static void debug(const T& msg) { Entry(DEBUG) << msg; } static Entry debug() { return Entry(DEBUG); } template static void trace(const T& msg) { Entry(TRACE) << msg; } static Entry trace() { return Entry(TRACE); } template static void log(const T& msg, Level lvl) { Entry(lvl) << msg; } static void log(Level lvl, std::stringbuf* strb); };