257 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#pragma once
 | 
						|
 | 
						|
#if !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)
 | 
						|
#define MIJIN_LOGGING_LOGGER_HPP_INCLUDED 1
 | 
						|
 | 
						|
#include <cstdint>
 | 
						|
#include <format>
 | 
						|
#include <source_location>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
#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<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
 | 
						|
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<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
 | 
						|
struct BaseLogChannel
 | 
						|
{
 | 
						|
    using char_t = TChar;
 | 
						|
 | 
						|
    const char_t* name;
 | 
						|
};
 | 
						|
 | 
						|
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
 | 
						|
 | 
						|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
 | 
						|
struct BaseLogMessage
 | 
						|
{
 | 
						|
    using char_t = TChar;
 | 
						|
 | 
						|
    const char_t* text;
 | 
						|
    const BaseLogChannel<char_t>* channel;
 | 
						|
    const BaseLogLevel<char_t>* level;
 | 
						|
    std::source_location sourceLocation;
 | 
						|
};
 | 
						|
 | 
						|
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
 | 
						|
 | 
						|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
 | 
						|
class BaseLogSink
 | 
						|
{
 | 
						|
public:
 | 
						|
    using char_t = TChar;
 | 
						|
    using message_t = BaseLogMessage<char_t>;
 | 
						|
 | 
						|
    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 T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR
 | 
						|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)>
 | 
						|
class BaseLogger
 | 
						|
{
 | 
						|
public:
 | 
						|
    using char_t = TChar;
 | 
						|
    using traits_t = TTraits;
 | 
						|
    using allocator_t = TAllocator<char_t>;
 | 
						|
 | 
						|
    using sink_t = BaseLogSink<char_t>;
 | 
						|
    using level_t = BaseLogLevel<char_t>;
 | 
						|
    using channel_t = BaseLogChannel<char_t>;
 | 
						|
    using message_t = BaseLogMessage<char_t>;
 | 
						|
    using string_t = std::basic_string<char_t, traits_t, allocator_t>;
 | 
						|
private:
 | 
						|
    std::vector<sink_t*, TAllocator<sink_t*>> mSinks;
 | 
						|
public:
 | 
						|
    explicit BaseLogger(TAllocator<sink_t*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<sink_t*>>)
 | 
						|
        : 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<typename... TArgs>
 | 
						|
    void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
 | 
						|
             std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
 | 
						|
             MIJIN_NOEXCEPT_IF(noexcept(std::declval<allocator_t>().allocate(1)))
 | 
						|
    {
 | 
						|
        string_t buffer(allocator_t(mSinks.get_allocator()));
 | 
						|
        std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
 | 
						|
        log(level, channel, std::move(sourceLocation), buffer.c_str());
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
#define LOGGER_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, 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<chr_type> 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<chr_type> 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<chr_type> 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<int>(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)
 |