174 lines
5.4 KiB
C++
174 lines
5.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
|
|
{
|
|
struct LogLevel
|
|
{
|
|
const char* name;
|
|
int value;
|
|
|
|
explicit operator int() const MIJIN_NOEXCEPT { return value; }
|
|
|
|
auto operator<=>(const LogLevel& other) const MIJIN_NOEXCEPT { return value <=> other.value; }
|
|
};
|
|
|
|
struct LogChannel
|
|
{
|
|
const char* name;
|
|
};
|
|
|
|
struct LogMessage
|
|
{
|
|
const char* text;
|
|
const LogChannel* channel;
|
|
const LogLevel* level;
|
|
std::source_location sourceLocation;
|
|
};
|
|
|
|
class LogSink
|
|
{
|
|
public:
|
|
virtual ~LogSink() noexcept = default;
|
|
|
|
virtual void handleMessage(const LogMessage& message) MIJIN_NOEXCEPT = 0;
|
|
};
|
|
|
|
template<template<typename T> typename TAllocator = std::allocator>
|
|
class Logger
|
|
{
|
|
private:
|
|
std::vector<LogSink*, TAllocator<LogSink*>> mSinks;
|
|
public:
|
|
explicit Logger(TAllocator<LogSink*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<LogSink*>>)
|
|
: mSinks(std::move(allocator))
|
|
{}
|
|
|
|
Logger(const Logger&) = default;
|
|
|
|
Logger(Logger&&) = default;
|
|
|
|
Logger& operator=(const Logger&) = default;
|
|
|
|
Logger& operator=(Logger&&) = default;
|
|
|
|
void addSink(LogSink& sink)
|
|
{
|
|
mSinks.push_back(&sink);
|
|
}
|
|
|
|
void postMessage(const LogMessage& message) const MIJIN_NOEXCEPT
|
|
{
|
|
for (LogSink* sink: mSinks)
|
|
{
|
|
sink->handleMessage(message);
|
|
}
|
|
}
|
|
|
|
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation, const char* msg) const MIJIN_NOEXCEPT
|
|
{
|
|
postMessage({
|
|
.text = msg,
|
|
.channel = &channel,
|
|
.level = &level,
|
|
.sourceLocation = std::move(sourceLocation)
|
|
});
|
|
}
|
|
|
|
template<typename... TArgs>
|
|
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation,
|
|
std::format_string<TArgs...> fmt, TArgs&& ... args) const
|
|
MIJIN_NOEXCEPT_IF(noexcept(std::declval<TAllocator<char>>().allocate(1)))
|
|
{
|
|
std::basic_string<char, std::char_traits<char>, TAllocator<char>> buffer(TAllocator<char>(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 MIJIN_DECLARE_LOG_CHANNEL(cnlName) \
|
|
namespace mijin_log_channel \
|
|
{ \
|
|
extern const ::mijin::LogChannel cnlName; \
|
|
}
|
|
|
|
#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) \
|
|
namespace mijin_log_channel \
|
|
{ \
|
|
const ::mijin::LogChannel cnlName { \
|
|
.name = #cnlName \
|
|
}; \
|
|
}
|
|
|
|
#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) \
|
|
namespace mijin_log_level \
|
|
{ \
|
|
inline constexpr ::mijin::LogLevel lvlName{ \
|
|
.name = #lvlName, \
|
|
.value = lvlValue \
|
|
}; \
|
|
}
|
|
|
|
MIJIN_DECLARE_LOG_CHANNEL(GENERAL)
|
|
MIJIN_DEFINE_LOG_LEVEL(DEBUG, -1000)
|
|
MIJIN_DEFINE_LOG_LEVEL(VERBOSE, -500)
|
|
MIJIN_DEFINE_LOG_LEVEL(INFO, 0)
|
|
MIJIN_DEFINE_LOG_LEVEL(WARNING, 500)
|
|
MIJIN_DEFINE_LOG_LEVEL(ERROR, 1000)
|
|
|
|
#if defined(MIJIN_MIN_LOGLEVEL)
|
|
inline constexpr int MIN_LOG_LEVEL = static_cast<int>(MIJIN_MIN_LOGLEVEL);
|
|
#elif defined(MIJIN_DEBUG)
|
|
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::DEBUG.value;
|
|
#else
|
|
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::VERBOSE.value;
|
|
#endif
|
|
|
|
#define MIJIN_IMPORT_LOG_DEFAULTS \
|
|
namespace mijin_log_channel \
|
|
{ \
|
|
using ::mijin::mijin_log_channel::GENERAL; \
|
|
} \
|
|
\
|
|
namespace mijin_log_level \
|
|
{ \
|
|
using ::mijin::mijin_log_level::DEBUG; \
|
|
using ::mijin::mijin_log_level::VERBOSE; \
|
|
using ::mijin::mijin_log_level::INFO; \
|
|
using ::mijin::mijin_log_level::WARNING; \
|
|
using ::mijin::mijin_log_level::ERROR; \
|
|
}
|
|
|
|
#define MIJIN_LOG_ALWAYS(level, channel, ...) mijin__getLogger__().log( \
|
|
mijin_log_level::level, mijin_log_channel::channel, std::source_location::current(), __VA_ARGS__ \
|
|
)
|
|
#define MIJIN_LOG(level, channel, ...) \
|
|
if constexpr (mijin_log_level::level.value < mijin::MIN_LOG_LEVEL) {} \
|
|
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
|
|
|
|
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
|
|
const auto& mijin__getLogger__() const noexcept \
|
|
{ \
|
|
return loggerExpr; \
|
|
}
|
|
#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \
|
|
auto mijin__getLogger__ = [&]() -> const auto& \
|
|
{ \
|
|
return loggerExpr; \
|
|
};
|
|
} // namespace mijin
|
|
|
|
|
|
#endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)
|