NBEngine/engine/NBCore/Logger.hpp
2026-06-11 04:24:06 -05:00

177 lines
4.4 KiB
C++

#pragma once
#ifndef _NB_LOGGER
#define _NB_LOGGER
#include <chrono>
#include <ostream>
#include <thread>
#include <unordered_map>
#include <vector>
#include "DataSink.hpp"
#include "Processes.hpp"
#include "ThreadSafeQueue.hpp"
#include "TypeTraits.hpp"
namespace nb {
template <typename T>
class ErrorBase;
typedef std::chrono::time_point<
std::chrono::system_clock,
std::chrono::nanoseconds
> LoggerTimePoint;
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
template<typename LogType, typename Logger, typename ST=std::ostream*>
class LoggerBase
: public MultithreadedDataProcessor<LogType, Logger>{
using StreamType = ST;
using LoggerType = Logger;
using Base = MultithreadedDataProcessor<LogType, LoggerType>;
public:
bool run() override {
if (!static_cast<LoggerType*>(this)->isRunning()) {
this->_running = true;
this->_runningThread = std::make_shared<std::thread>([&]{
while(static_cast<LoggerType*>(this)->isRunning()) {
static_cast<LoggerType*>(this)->flush();
}
static_cast<LoggerType*>(this)->flush();
});
}
return static_cast<LoggerType*>(this)->isRunning();
}
protected:
LoggerBase() = default;
StreamType _ostream;
};
struct LogEvent{
const LoggerTimePoint time;
const unsigned char lvl;
const std::string msg;
const std::thread::id tid;
const uint64_t pid;
const std::string file="";
const unsigned int line=0;
};
template <typename LT>
class DebugLogger : public LoggerBase<LogEvent, LT, std::vector<std::ostream*>>{
using StreamType = std::vector<std::ostream*>;
using LoggerType = LT;
using Base = LoggerBase<LogEvent, LoggerType, StreamType>;
public:
template<typename... ST>
DebugLogger(ST&... streams) : _ostream(nb::RefPackToPtrVec<std::ostream, ST...>(streams...).vec) {}
~DebugLogger() { static_cast<LoggerType*>(this)->stop(); }
void log(
std::string msg,
uint8_t lvl=0x00,
std::string file="",
unsigned int line=0
) {
static_cast<LoggerType*>(this)->push(LogEvent{
std::chrono::system_clock::now(),
lvl,
msg,
std::this_thread::get_id(),
GetPID(),
file,
line
});
}
template <size_t N>
void log(
char const(&msg) [N],
uint8_t lvl=0x00,
std::string file="",
unsigned int line=0
) {
static_cast<LoggerType*>(this)->log(std::string(msg), lvl, file, line);
}
template <typename T>
void log(
const ErrorBase<T>& err,
uint8_t lvl=0xFF,
std::string file="",
unsigned int line=0
) {
static_cast<LoggerType*>(this)->log(err.what(), lvl, file, line);
}
template <typename T>
std::enable_if_t<std::is_base_of_v<std::exception, T>, void> log(
const T& err,
uint8_t lvl=0xFF,
std::string file="",
unsigned int line=0
) {
static_cast<LoggerType*>(this)->log(std::string(err.what()), lvl, file, line);
}
template<typename U>
void warn(
U val,
uint8_t lvl=0x01,
std::string file="",
unsigned int line=0
) { static_cast<LoggerType*>(this)->log(val, lvl, file, line); }
template<typename U>
void error(
U val,
std::string file="",
unsigned int line=0
) {
static_cast<LoggerType*>(this)->log(val, 0xFF, file, line);
static_cast<LoggerType*>(this)->flush();
}
protected:
std::vector<std::ostream*> _ostream;
};
class DefaultDebugLogger : public DebugLogger<DefaultDebugLogger> {
using LoggerType = DefaultDebugLogger;
using Base = DebugLogger<DefaultDebugLogger>;
template <typename... Ts>
struct LogRow;
public:
using Base::Base;
~DefaultDebugLogger() { stop(); }
friend class BufferedDataProcessor<LogEvent, ThreadsafeQueue<LogEvent>, DefaultDebugLogger>;
template <typename STR, typename... Ts>
friend STR& operator<<(STR&, const DefaultDebugLogger::LogRow<Ts...>&);
protected:
using Base::_ostream;
bool process(const LogEvent& msg);
};
#ifndef _NB_NO_LOGGER
extern DefaultDebugLogger logger;
#endif // _NB_NO_LOGGER
// Taking Charge of Adult ADHD by Russell Barkley
} // namespace nb
#endif // _NB_LOGGER