#pragma once #if !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED) #define MIJIN_LOGGING_LOGGER_HPP_INCLUDED 1 #include #include #include #include #include #include "../internal/common.hpp" #include "../util/annot.hpp" namespace mijin { #if !defined(MIJIN_LOG_LEVEL_VALUE_DEBUG) #define MIJIN_LOG_LEVEL_VALUE_DEBUG -1000 #endif #if !defined(MIJIN_LOG_LEVEL_VALUE_VERBOSE) #define MIJIN_LOG_LEVEL_VALUE_VERBOSE -500 #endif #if !defined(MIJIN_LOG_LEVEL_VALUE_INFO) #define MIJIN_LOG_LEVEL_VALUE_INFO 0 #endif #if !defined(MIJIN_LOG_LEVEL_VALUE_WARNING) #define MIJIN_LOG_LEVEL_VALUE_WARNING 500 #endif #if !defined(MIJIN_LOG_LEVEL_VALUE_ERROR) #define MIJIN_LOG_LEVEL_VALUE_ERROR 1000 #endif #if !defined(MIJIN_FUNCNAME_GET_LOGGER) #define MIJIN_FUNCNAME_GET_LOGGER mijin__getLogger__ #endif #if !defined(MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE) #define MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE mijin__getMinLogLevelCompile #endif #if !defined(MIJIN_NSNAME_LOG_LEVEL) #define MIJIN_NSNAME_LOG_LEVEL mijin_log_level #endif #if !defined(MIJIN_NSNAME_LOG_CHANNEL) #define MIJIN_NSNAME_LOG_CHANNEL mijin_log_channel #endif template struct BaseLogLevel { using char_t = TChar; const char_t* name; int value; explicit operator int() const MIJIN_NOEXCEPT { return value; } auto operator<=>(const BaseLogLevel& other) const MIJIN_NOEXCEPT { return value <=> other.value; } }; MIJIN_DEFINE_CHAR_VERSIONS(LogLevel) template struct BaseLogChannel { using char_t = TChar; const char_t* name; }; MIJIN_DEFINE_CHAR_VERSIONS(LogChannel) template struct BaseLogMessage { using char_t = TChar; const char_t* text; const BaseLogChannel* channel; const BaseLogLevel* level; std::source_location sourceLocation; }; MIJIN_DEFINE_CHAR_VERSIONS(LogMessage) template class BaseLogSink { public: using char_t = TChar; using message_t = BaseLogMessage; virtual ~BaseLogSink() noexcept = default; virtual void handleMessage(const message_t& message) MIJIN_NOEXCEPT = 0; }; MIJIN_DEFINE_CHAR_VERSIONS(LogSink) #define LOGGER_COMMON_ARGS(chr_type) template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR template, LOGGER_COMMON_ARGS(TChar)> class BaseLogger { public: using char_t = TChar; using traits_t = TTraits; using allocator_t = TAllocator; using sink_t = BaseLogSink; using level_t = BaseLogLevel; using channel_t = BaseLogChannel; using message_t = BaseLogMessage; using string_t = std::basic_string; private: std::vector> mSinks; public: explicit BaseLogger(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) : mSinks(std::move(allocator)) {} BaseLogger(const BaseLogger&) = default; BaseLogger(BaseLogger&&) = default; BaseLogger& operator=(const BaseLogger&) = default; BaseLogger& operator=(BaseLogger&&) = default; void addSink(sink_t& sink) { mSinks.push_back(&sink); } void postMessage(const message_t& message) const MIJIN_NOEXCEPT { for (sink_t* sink: mSinks) { sink->handleMessage(message); } } void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, const char_t* msg) const MIJIN_NOEXCEPT { postMessage({ .text = msg, .channel = &channel, .level = &level, .sourceLocation = std::move(sourceLocation) }); } template void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, std::basic_format_string...> fmt, TArgs&& ... args) const MIJIN_NOEXCEPT_IF(noexcept(std::declval().allocate(1))) { string_t buffer(allocator_t(mSinks.get_allocator())); std::format_to(std::back_inserter(buffer), fmt, std::forward(args)...); log(level, channel, std::move(sourceLocation), buffer.c_str()); } }; #define LOGGER_SET_ARGS(chr_type) chr_type, std::char_traits, TAllocator MIJIN_DEFINE_CHAR_VERSIONS_TMPL(Logger, LOGGER_COMMON_ARGS, LOGGER_SET_ARGS) #undef LOGGER_COMMON_ARGS #undef LOGGER_SET_ARGS #define MIJIN_DECLARE_LOG_CHANNEL_BASE(chr_type, cnlName) \ namespace MIJIN_NSNAME_LOG_CHANNEL \ { \ extern const ::mijin::BaseLogChannel cnlName; \ } #define MIJIN_DECLARE_LOG_CHANNEL(cnlName) MIJIN_DECLARE_LOG_CHANNEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, cnlName) #define MIJIN_DEFINE_LOG_CHANNEL_BASE(chr_type, cnlName) \ namespace MIJIN_NSNAME_LOG_CHANNEL \ { \ const ::mijin::BaseLogChannel cnlName { \ .name = MIJIN_SMART_STRINGIFY(chr_type, cnlName) \ }; \ } #define MIJIN_DEFINE_LOG_CHANNEL(cnlName) MIJIN_DEFINE_LOG_CHANNEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, cnlName) #define MIJIN_DEFINE_LOG_LEVEL_BASE(chr_type, lvlName, lvlValue) \ namespace MIJIN_NSNAME_LOG_LEVEL \ { \ inline constexpr ::mijin::BaseLogLevel lvlName{ \ .name = MIJIN_SMART_STRINGIFY(chr_type, lvlName), \ .value = lvlValue \ }; \ } #define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) MIJIN_DEFINE_LOG_LEVEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, lvlName, lvlValue) #if defined(MIJIN_MIN_LOGLEVEL_COMPILE) inline constexpr int MIN_LOG_LEVEL_COMPILE = static_cast(MIJIN_MIN_LOGLEVEL_COMPILE); #elif defined(MIJIN_DEBUG) inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_DEBUG; #else inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_VERBOSE; #endif #define MIJIN_DEFINE_DEFAULT_LOG_EVELS \ MIJIN_DEFINE_LOG_LEVEL(DEBUG, MIJIN_LOG_LEVEL_VALUE_DEBUG) \ MIJIN_DEFINE_LOG_LEVEL(VERBOSE, MIJIN_LOG_LEVEL_VALUE_VERBOSE) \ MIJIN_DEFINE_LOG_LEVEL(INFO, MIJIN_LOG_LEVEL_VALUE_INFO) \ MIJIN_DEFINE_LOG_LEVEL(WARNING, MIJIN_LOG_LEVEL_VALUE_WARNING) \ MIJIN_DEFINE_LOG_LEVEL(ERROR, MIJIN_LOG_LEVEL_VALUE_ERROR) #define MIJIN_LOG_LEVEL_OBJECT(level) MIJIN_NSNAME_LOG_LEVEL::level #define MIJIN_LOG_CHANNEL_OBJECT(channel) MIJIN_NSNAME_LOG_CHANNEL::channel #define MIJIN_LOG_ALWAYS(level, channel, ...) MIJIN_FUNCNAME_GET_LOGGER().log( \ MIJIN_LOG_LEVEL_OBJECT(level), MIJIN_LOG_CHANNEL_OBJECT(channel), std::source_location::current(), __VA_ARGS__ \ ) #define MIJIN_LOG(level, channel, ...) \ if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \ else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__) #define MIJIN_SET_CLASS_LOGGER(loggerExpr) \ const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \ { \ return loggerExpr; \ } #define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \ auto MIJIN_FUNCNAME_GET_LOGGER = [&]() -> const auto& \ { \ return loggerExpr; \ }; #define MIJIN_SET_CLASS_MIN_LOG_LEVEL_COMPILE(level) \ int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT \ { \ return MIJIN_LOG_LEVEL_OBJECT(level).value; \ } #define MIJIN_SET_SCOPE_MIN_LOG_LEVEL_COMPILE(level) \ auto MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE = []() -> int \ { \ return MIJIN_LOG_LEVEL_OBJECT(level).value; \ }; } // namespace mijin inline constexpr int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT { return ::mijin::MIN_LOG_LEVEL_COMPILE; } #endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)