NBEngine/engine/NBCore/Logger.hpp

164 lines
3.9 KiB
C++

#pragma once
#ifndef _NB_LOGGER
#define _NB_LOGGER
#include <atomic>
#include <chrono>
#include <ostream>
#include <thread>
#include <unordered_map>
#include <vector>
#include "Processes.hpp"
#include "ThreadSafeQueue.hpp"
namespace nb {
typedef std::chrono::time_point<
std::chrono::system_clock,
std::chrono::nanoseconds
> LoggerTimePoint;
struct LogEvent{
const LoggerTimePoint time;
const unsigned char lvl;
const std::string msg;
const std::thread::id tid;
const uint64_t pid;
};
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
template<typename LoggerName, typename ST=std::ostream>
class LoggerBase {
public:
using Type = LoggerName;
using StreamType = ST;
LoggerBase(StreamType& stream) : _ostreams({&stream}) {}
LoggerBase(const LoggerBase&) = delete;
LoggerBase(LoggerBase&&) = delete;
LoggerBase& operator=(const LoggerBase&) = delete;
~LoggerBase() {
stop();
}
bool isRunning() const noexcept {
return _running && bool(_runningThread);
}
unsigned int count() const {
return _queue.size();
}
bool run() {
if (!isRunning()) {
_running = true;
_runningThread = std::make_shared<std::thread>([&]{
auto this_type = static_cast<Type*>(this);
while(this_type->isRunning()) {
this_type->flush();
}
});
}
return isRunning();
}
bool stop() noexcept {
if (isRunning()) {
_running = false;
_runningThread->join();
_runningThread = nullptr;
flush();
}
return isRunning();
}
LogEvent pop() {
std::shared_ptr<LogEvent> event;
_queue.pop(event);
static_cast<Type*>(this)->process(*event);
return *event;
}
void flush() noexcept {
while(_queue.size()) {
pop();
}
}
void deleteAll() {
_queue.empty();
}
template <typename T>
void log(const T&, const uint8_t);
void log(const std::string& msg, const uint8_t& lvl) {
_queue.push(LogEvent{
std::chrono::system_clock::now(),
lvl,
msg,
std::this_thread::get_id(),
GetPID(),
});
}
template <size_t N>
void log(char const(&msg) [N], const uint8_t& lvl) {
static_cast<Type*>(this)->log(std::string(msg), lvl);
}
void log(const std::exception& err, const uint8_t& lvl) {
_queue.push(LogEvent{
std::chrono::system_clock::now(),
lvl,
err.what(),
std::this_thread::get_id(),
GetPID(),
});
}
void msg(const std::string& msg, const uint8_t& lvl=0x00) {
static_cast<Type*>(this)->log(msg, lvl);
}
void warn(const std::string& msg, const uint8_t& lvl=0x01) {
static_cast<Type*>(this)->log(msg, lvl);
}
void warn(const std::exception& err, const uint8_t lvl=0x01) {
static_cast<Type*>(this)->log(err, lvl);
}
void error(const std::string& msg, const uint8_t lvl=0xFF) {
static_cast<Type*>(this)->log(msg, lvl);
}
void error(const std::exception& err, const uint8_t lvl=0xFF) {
static_cast<Type*>(this)->log(err, lvl);
}
protected:
void process(const LogEvent&);
ThreadsafeQueue<LogEvent> _queue;
std::vector<StreamType*> const _ostreams;
std::atomic<bool> _running;
std::shared_ptr<std::thread> _runningThread = nullptr;
};
class BasicLogger : public LoggerBase<BasicLogger> {
public:
using Base = LoggerBase<BasicLogger>;
BasicLogger(std::ostream& stream) : Base(stream) {}
virtual void process(const LogEvent& event) {
*_ostreams[0] << event.msg << "\n";
}
};
} // namespace nb
#endif // _NB_LOGGER