namespace instead of uber class, small improvements
This commit is contained in:
parent
9d2aa6a77d
commit
1399770a6c
237
Log.cpp
237
Log.cpp
|
@ -1,49 +1,113 @@
|
|||
#include "Log.h"
|
||||
|
||||
#include <chrono> // date/time
|
||||
#include <fstream> // ofstream (logging to file)
|
||||
#include <iomanip> // std::put_time
|
||||
#include <iostream> // std::ostream, std::cout, std::cin
|
||||
#include <vector> // list of outputs
|
||||
|
||||
namespace Log {
|
||||
|
||||
/*
|
||||
* class Log
|
||||
* abstract class Output
|
||||
* default implementations
|
||||
*/
|
||||
|
||||
// static members
|
||||
std::vector<Log::Output*> 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<Output*> 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
|
162
Log.h
162
Log.h
|
@ -1,123 +1,63 @@
|
|||
#pragma once
|
||||
#include <chrono> // date/time
|
||||
#include <fstream> // ofstream (logging to file)
|
||||
#include <iomanip> // std::put_time
|
||||
#include <iostream> // std::ostream, std::cout, std::cin
|
||||
#include <sstream> // std::stringstream (buffer for log entries)
|
||||
|
||||
#include <sstream> // std::stringstream (buffer for log entries)
|
||||
#include <string>
|
||||
#include <vector> // 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<Output*> 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 <typename T>
|
||||
Entry& operator<<(const T& msg) {
|
||||
ss << msg;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class LeveledSink {
|
||||
private:
|
||||
Log::Level level;
|
||||
public:
|
||||
LeveledSink(Log::Level level);
|
||||
|
||||
template <typename T>
|
||||
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 <typename T> Entry& operator<<(const T& msg) {
|
||||
ss << msg;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class LeveledSink {
|
||||
private:
|
||||
Level level;
|
||||
|
||||
public:
|
||||
LeveledSink(Level level);
|
||||
|
||||
template <typename T> 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
|
||||
|
|
Loading…
Reference in New Issue