Files
2026-10-23 16:13:31 +00:00

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