167 lines
4.5 KiB
C++
167 lines
4.5 KiB
C++
#pragma once
|
|
|
|
#include "ddkit/utils/container.hpp"
|
|
#include "ddkit/utils/system.hpp"
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <queue>
|
|
#include <memory>
|
|
#include <functional>
|
|
|
|
// platforms either define __FUNCSIG__ (MSVC), or __PRETTY_FUNCTION__ (gcc, clang)
|
|
#ifdef _WIN32
|
|
#define DDKIT_LOG_FUNCSIG __FUNCSIG__
|
|
#else
|
|
#define DDKIT_LOG_FUNCSIG __PRETTY_FUNCTION__
|
|
#endif
|
|
|
|
#define DDKIT_LOG_LOCATION (ddkit::utils::system::filename(std::string(__FILE__)) + ":" + __LINE__)
|
|
#define DDKIT_LOG_LOCATION_STREAM ddkit::utils::system::filename(std::string(__FILE__)) << ":" << __LINE__ << ":" << DDKIT_LOG_FUNCSIG
|
|
#define DDKIT_LOG_VAR_VALUE(val) #val << "==" << (val)
|
|
|
|
// Only for trace, there would be anyway a macro needed for line
|
|
#define DDKIT_LOG_TRACE ddkit::utils::log::trace() << "(" << DDKIT_LOG_LOCATION_STREAM << ") "
|
|
|
|
namespace ddkit {
|
|
namespace base {
|
|
|
|
class IConfig;
|
|
class Config;
|
|
}
|
|
|
|
namespace utils {
|
|
namespace log {
|
|
|
|
enum class Level {
|
|
None = 0x0, // 0b00000000
|
|
Fatal = 0x1, // 0b00000001
|
|
Error = 0x3, // 0b00000011
|
|
Warn = 0x7, // 0b00000111
|
|
Info = 0xF, // 0b00001111
|
|
// ?? = 0x1F, // 0b00011111 RESERVED
|
|
Debug = 0x3F, // 0b00111111
|
|
// ?? = 0x7F, // 0b01111111 RESERVED
|
|
Trace = 0xFF, // 0b11111111
|
|
};
|
|
|
|
class Sink;
|
|
typedef std::shared_ptr<Sink> SinkPtr;
|
|
|
|
class Logger {
|
|
|
|
using SinkFactoryFunction = std::function<SinkPtr(const base::IConfig&)>;
|
|
|
|
public:
|
|
static void loadConfig(const base::Config&);
|
|
static void registerSinkType(const std::string& id, const SinkFactoryFunction& factory);
|
|
static void installSink(const std::string& id, const base::IConfig&);
|
|
static void installSink(SinkPtr);
|
|
static void write(Level, const std::string&);
|
|
static void uninstallAllSinks();
|
|
static Level getHighestUsedLevel();
|
|
private:
|
|
Logger();
|
|
Logger(const Logger&) = delete;
|
|
static Logger& instance();
|
|
static std::string getPrefix(Level);
|
|
|
|
// shared_ptr/SinkPtr is used to circumvent the issue of having a collection of pure virtual class
|
|
std::vector<SinkPtr> m_sinks;
|
|
std::map<std::string, SinkFactoryFunction> m_sinkTypes;
|
|
|
|
// stored for performance reasons, aids in aborting early in LineBuilder if the log level isnt used
|
|
Level mHighestLevel;
|
|
};
|
|
|
|
class Sink {
|
|
|
|
public:
|
|
template<class Type> class Registrar;
|
|
Sink(const base::IConfig&);
|
|
virtual ~Sink(){}
|
|
virtual void write(Level level, const std::string& prefix, const std::string& line) = 0;
|
|
|
|
void setLevel(Level);
|
|
void setLevel(const std::string&);
|
|
Level getLevel() const;
|
|
|
|
private:
|
|
Sink() = delete;
|
|
|
|
protected:
|
|
Level m_level;
|
|
};
|
|
|
|
template<class Type> class Sink::Registrar {
|
|
|
|
public:
|
|
Registrar() = delete;
|
|
// Registrar<FileSink>("file")
|
|
Registrar(const std::string& id){
|
|
Logger::registerSinkType(id, [](const base::IConfig& config){
|
|
return std::make_shared<Type>(config);
|
|
});
|
|
}
|
|
|
|
// Registrar<OStreamSink>("cerr", std::cerr)
|
|
template<class Param> Registrar(const std::string& id, Param& param){
|
|
auto pRef = std::ref(param);
|
|
Logger::registerSinkType(id, [pRef](const base::IConfig& config){
|
|
return std::make_shared<Type>(config, pRef.get());
|
|
});
|
|
}
|
|
};
|
|
|
|
/// @brief Stores a number of strings and writes them to log on destruction
|
|
class LineBuilder {
|
|
|
|
public:
|
|
LineBuilder(Level);
|
|
LineBuilder() = delete;
|
|
LineBuilder(const LineBuilder&) = delete;
|
|
LineBuilder(LineBuilder&&);
|
|
~LineBuilder();
|
|
LineBuilder& operator<<(const std::string&);
|
|
LineBuilder& operator<<(bool);
|
|
|
|
template<typename T> LineBuilder& operator<<(const T& value){
|
|
if(Logger::getHighestUsedLevel() < m_level) return *this; // early abort for performance gains
|
|
|
|
m_line << value << " ";
|
|
return *this;
|
|
}
|
|
|
|
template<typename T> LineBuilder& operator<<(const std::vector<T>& vec){
|
|
if(Logger::getHighestUsedLevel() < m_level) return *this; // early abort for performance gains
|
|
|
|
m_line << "[";
|
|
for(auto it = vec.begin(); it != vec.end(); it++){
|
|
*this << *it;
|
|
if(vec.end() - it != 1){ // all except for the last element
|
|
m_line << ", ";
|
|
}
|
|
}
|
|
*this << "] ";
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Level m_level;
|
|
std::ostringstream m_line;
|
|
};
|
|
|
|
LineBuilder fatal();
|
|
LineBuilder error();
|
|
LineBuilder warn();
|
|
LineBuilder info();
|
|
LineBuilder debug();
|
|
LineBuilder trace();
|
|
|
|
} // namespace log
|
|
} // namespace utils
|
|
|
|
namespace log = ddkit::utils::log;
|
|
|
|
} // namespace ddkit
|