Log/Log.h

146 lines
3.7 KiB
C++

#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 <string>
#include <vector> // 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<Output*> 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 <typename T>
Entry& operator<<(const T& msg) {
ss << msg;
return *this;
}
};
template <typename T>
static void fatal(const T& msg) {
Entry(FATAL) << msg;
}
static Entry fatal() { return Entry(FATAL); }
template <typename T>
static void error(const T& msg) {
Entry(ERROR) << msg;
}
static Entry error() { return Entry(ERROR); }
template <typename T>
static void warn(const T& msg) {
Entry(WARN) << msg;
}
static Entry warn() { return Entry(WARN); }
template <typename T>
static void note(const T& msg) {
Entry(NOTE) << msg;
}
static Entry note() { return Entry(NOTE); }
template <typename T>
static void info(const T& msg) {
Entry(INFO) << msg;
}
static Entry info() { return Entry(INFO); }
template <typename T>
static void debug(const T& msg) {
Entry(DEBUG) << msg;
}
static Entry debug() { return Entry(DEBUG); }
template <typename T>
static void trace(const T& msg) {
Entry(TRACE) << msg;
}
static Entry trace() { return Entry(TRACE); }
template <typename T>
static void log(const T& msg, Level lvl) {
Entry(lvl) << msg;
}
static void log(Level lvl, std::stringbuf* strb);
};