#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 #if LOG_USEMUTEX == 1 #include #endif namespace Log { /* * abstract class Output * default implementations */ // abstract base class for a log sink class Output { public: Output() {} Output(Level lvl_max) : lvl_max(lvl_max) {} virtual ~Output() {} virtual void log(Level lvl, std::stringbuf* sbuf) { //aquire lock #if LOG_USEMUTEX == 1 std::unique_lock lock(ostreamLock); #endif std::ostream* os = getOs(lvl); if (os) { *os << sbuf << std::endl; } }; virtual void setLogLevel(Level lvl) { lvl_max = lvl; } 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 #if LOG_USEMUTEX == 1 std::mutex ostreamLock; //used for both streams #endif }; // logging to stdout/stderr class ConsoleOutput : public Output { public: ConsoleOutput() : Output() {} virtual bool setColoredOutput(bool enabled) { // TODO: check the terminal's compatibility for colors coloredOutput = enabled; return true; } private: std::ostream* osStd = &std::cout; std::ostream* osErr = &std::cerr; bool coloredOutput; 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"}; //aquire lock #if LOG_USEMUTEX == 1 std::unique_lock lock(ostreamLock); #endif 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; #if LOG_USEMUTEX == 1 std::mutex ostreamLock; //used for both streams #endif 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 strb->pubseekpos(0); } } LeveledSink fatal(Level::FATAL); LeveledSink error(Level::ERROR); LeveledSink warn(Level::WARN); LeveledSink note(Level::NOTE); LeveledSink info(Level::INFO); LeveledSink debug(Level::DEBUG); LeveledSink trace(Level::TRACE); /* * class Entry */ Entry::Entry(Level lvl) : lvl(lvl) { addMetadataHeader(); } Entry::~Entry() { log(lvl, ss.rdbuf()); } void Entry::addMetadataHeader() { static const char* LevelTag[] = {"", "[FATAL] ", "[ERROR] ", "[WARN ] ", "[NOTE ] ", "[INFO ] ", "[DEBUG] ", "[TRACE] "}; // get current date/time std::time_t now = std::time(NULL); // datetime ss << "[" << std::put_time(std::localtime(&now), "%F %T") << "]"; // log level ss << LevelTag[lvl]; } void init() { // add default console logger if (outputs.empty()) outputs.push_back(new ConsoleOutput()); } void stop() { for (auto output : outputs) delete output; outputs.clear(); } void addLogfile(const std::string& filename, Level max) { outputs.push_back(new FileOutput(filename, max)); } void addLogfile(const std::string& filename, Level min, Level max) { outputs.push_back(new FileOutput(filename, min, max)); } 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