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)
|