#pragma once #ifndef _NB_LOGGER #define _NB_LOGGER #include #include #include #include #include #include "DataSink.hpp" #include "Errors.hpp" #include "Processes.hpp" #include "ThreadSafeQueue.hpp" #include "TypeTraits.hpp" namespace nb { 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 LogProcessFunctionMap; template class LoggerBase : public MultithreadedDataProcessor{ using StreamType = ST; using LoggerType = Logger; using Base = MultithreadedDataProcessor; public: bool run() override { if (!static_cast(this)->isRunning()) { this->_running = true; this->_runningThread = std::make_shared([&]{ while(static_cast(this)->isRunning()) { static_cast(this)->flush(); } static_cast(this)->flush(); }); } return static_cast(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 class DebugLogger : public LoggerBase>{ using StreamType = std::vector; using LoggerType = LT; using Base = LoggerBase; public: template DebugLogger(ST&... streams) : _ostream(nb::RefPackToPtrVec(streams...).vec) {} ~DebugLogger() { static_cast(this)->stop(); } void log( std::string msg, uint8_t lvl=0x00, std::string file="", unsigned int line=0 ) { static_cast(this)->push(LogEvent{ std::chrono::system_clock::now(), lvl, msg, std::this_thread::get_id(), GetPID(), file, line }); } template void log( char const(&msg) [N], uint8_t lvl=0x00, std::string file="", unsigned int line=0 ) { static_cast(this)->log(std::string(msg), lvl, file, line); } template void log( const ErrorBase& err, uint8_t lvl=0xFF, std::string file="", unsigned int line=0 ) { static_cast(this)->log(err.what(), lvl, file, line); } template std::enable_if_t, void> log( const T& err, uint8_t lvl=0xFF, std::string file="", unsigned int line=0 ) { static_cast(this)->log(std::string(err.what()), lvl, file, line); } template void warn( U val, uint8_t lvl=0x01, std::string file="", unsigned int line=0 ) { static_cast(this)->log(val, lvl, file, line); } template void error( U val, std::string file="", unsigned int line=0 ) { static_cast(this)->log(val, 0xFF, file, line); } protected: std::vector _ostream; }; class DefaultDebugLogger : public DebugLogger { using LoggerType = DefaultDebugLogger; using Base = DebugLogger; template struct LogRow; public: using Base::Base; ~DefaultDebugLogger() { stop(); } friend class BufferedDataProcessor, DefaultDebugLogger>; template friend STR& operator<<(STR&, const DefaultDebugLogger::LogRow&); 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 /* template struct NB_DEFAULT_LOGGER_THROW; template<> struct NB_DEFAULT_LOGGER_THROW { template NB_DEFAULT_LOGGER_THROW(T arg) { #ifdef _NB_AUTOLOG logger.error(arg); #endif // _NB_AUTLOG throw arg; } }; template struct NB_DEFAULT_LOGGER_THROW { template NB_DEFAULT_LOGGER_THROW(Args&&... args) { std::shared_ptr error_ptr; #ifdef _NB_AUTOLOG #ifdef _NB_CODE_ERROR_LOCATIONS error_ptr = std::make_shared(args..., __LINE__, __FILE__); #else error_ptr = std::make_shared(args...); #endif // _NB_CODE_ERROR_LOCATIONS logger.error(*error_ptr); #endif // _NB_AUTLOG throw *error_ptr; } }; */ /* template void NB_DEFAULT_LOGGER_THROW(Args&& ... args) { std::shared_ptr error_ptr; #ifdef _NB_AUTOLOG #ifdef _NB_CODE_ERROR_LOCATIONS error_ptr = std::make_shared(args..., __LINE__, __FILE__); #else error_ptr = std::make_shared(args...); #endif // _NB_CODE_ERROR_LOCATIONS logger.error(*error_ptr); #endif // _NB_AUTLOG throw *error_ptr; } template void NB_DEFAULT_LOGGER_THROW(const Arg& arg) { #ifdef _NB_AUTOLOG logger.error(arg); #endif // _NB_AUTLOG throw arg; } */ template void NB_DEFAULT_LOGGER_THROW(const T& err, std::string file="", unsigned int line = 0) { #ifdef _NB_AUTOLOG if (file.empty()) { logger.error(err); } else { logger.error(err, file, line); } #endif // _NB_AUTLOG throw err; } } // namespace nb #ifdef _NB_AUTOLOG #ifdef _NB_CODE_ERROR_LOCATIONS #ifndef LOG #define LOG(args...) nb::logger.log(args, __FILE__, __LINE__) #endif // LOG #ifndef WARN #define WARN(args...) nb::logger.warn(args, __FILE__, __LINE__) #endif // WARN #ifndef ERROR #define ERROR(args...) nb::logger.error(args, __FILE__, __LINE__) #endif // ERROR #else #ifndef LOG #define LOG(args...) nb::logger.log(args) #endif // LOG #ifndef WARN #define WARN(args...) nb::logger.warn(args) #endif // WARN #ifndef ERROR #define ERROR(args...) nb::logger.error(args) #endif // ERROR #endif // _NB_CODE_ERROR_LOCATIONS #endif // _NB_AUTOLOG #ifndef THROW #ifdef _NB_AUTOLOG #define THROW(args...) ERROR(args); throw args #else #define THROW(args...) throw args #endif // _NB_CODE_ERROR_LOCATIONS #endif // THROW #endif // _NB_LOGGER