#include "Log.h" /* * class Log */ // static members std::vector Log::outputs; 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); void Log::init() { // add default console logger if (outputs.empty()) outputs.push_back(new Log::ConsoleOutput()); } void Log::stop() { for (auto output : outputs) delete output; outputs.clear(); } void Log::addLogfile(const std::string& filename, Level max) { outputs.push_back(new Log::FileOutput(filename, max)); } void Log::addLogfile(const std::string& filename, Level min, Level max) { outputs.push_back(new Log::FileOutput(filename, min, max)); } void Log::setConsoleLogLevel(Level lvl) { outputs.at(0)->setLogLevel(lvl); // has to exist } void Log::setColoredOutput(bool enabled) { ((Log::ConsoleOutput*) outputs.at(0))->setColoredOutput(enabled); // has to exist } void Log::log(Level lvl, std::stringbuf* strb) { for (Output* out : outputs) { out->log(lvl, strb); // reset stringbuffer read pointer to the beginning strb->pubseekpos(0); } } /* * abstract class Output * default implementations */ 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; } } void Log::Output::setLogLevel(Log::Level lvl) { lvl_max = lvl; } void Log::Output::printLogHeader(std::ostream* os, Level lvl) { static const char* LevelTag[] = {"", "[FATAL] ", "[ERROR] ", "[WARN ] ", "[NOTE ] ", "[INFO ] ", "[DEBUG] ", "[TRACE] "}; // get current date/time 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]; } /* * class ConsoleOutput */ Log::ConsoleOutput::ConsoleOutput() : Output() {} bool Log::ConsoleOutput::setColoredOutput(bool enabled) { // TODO: check the terminals compatibility for colors coloredOutput = enabled; return true; } 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; } } 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; } /* * 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; } Log::LeveledSink::LeveledSink(Log::Level level) : level(level) {}