#pragma once #include "ddkit/utils/container.hpp" #include "ddkit/utils/system.hpp" #include #include #include #include #include // 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 SinkPtr; class Logger { using SinkFactoryFunction = std::function; 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 m_sinks; std::map 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 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 Sink::Registrar { public: Registrar() = delete; // Registrar("file") Registrar(const std::string& id){ Logger::registerSinkType(id, [](const base::IConfig& config){ return std::make_shared(config); }); } // Registrar("cerr", std::cerr) template Registrar(const std::string& id, Param& param){ auto pRef = std::ref(param); Logger::registerSinkType(id, [pRef](const base::IConfig& config){ return std::make_shared(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 LineBuilder& operator<<(const T& value){ if(Logger::getHighestUsedLevel() < m_level) return *this; // early abort for performance gains m_line << value << " "; return *this; } template LineBuilder& operator<<(const std::vector& 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