Extra2D/Extra2D/include/extra2d/services/logger_service.h

213 lines
6.8 KiB
C++

#pragma once
#include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h>
#include <string>
#include <type_traits>
namespace extra2d {
/**
* @brief 日志颜色
*/
struct LogColor {
u8 r, g, b;
constexpr LogColor() : r(255), g(255), b(255) {}
constexpr LogColor(u8 r, u8 g, u8 b) : r(r), g(g), b(b) {}
static constexpr LogColor White() { return {255, 255, 255}; }
static constexpr LogColor Gray() { return {128, 128, 128}; }
static constexpr LogColor Red() { return {255, 85, 85}; }
static constexpr LogColor Green() { return {85, 255, 85}; }
static constexpr LogColor Yellow() { return {255, 255, 85}; }
static constexpr LogColor Blue() { return {85, 85, 255}; }
static constexpr LogColor Magenta() { return {255, 85, 255}; }
static constexpr LogColor Cyan() { return {85, 255, 255}; }
static constexpr LogColor SkyLight() { return {125, 211, 252}; }
static constexpr LogColor IndigoLight() { return {165, 180, 252}; }
};
/**
* @brief 日志级别
*/
enum class LogLevel : u8 {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5,
Off = 6
};
/**
* @brief 日志服务接口
*/
class ILogger : public IService {
public:
virtual ~ILogger() = default;
virtual void level(LogLevel lvl) = 0;
virtual LogLevel level() const = 0;
virtual bool enabled(LogLevel lvl) const = 0;
virtual void log(LogLevel lvl, const char *fmt, ...) = 0;
virtual void log(LogLevel lvl, const std::string &msg) = 0;
virtual void trace(const char *fmt, ...) = 0;
virtual void debug(const char *fmt, ...) = 0;
virtual void info(const char *fmt, ...) = 0;
virtual void warn(const char *fmt, ...) = 0;
virtual void error(const char *fmt, ...) = 0;
virtual void fatal(const char *fmt, ...) = 0;
virtual void levelColor(LogLevel lvl, const LogColor &c) = 0;
virtual LogColor levelColor(LogLevel lvl) const = 0;
virtual void colors(bool on) = 0;
virtual bool colors() const = 0;
ServiceInfo info() const override {
ServiceInfo i;
i.name = "Logger";
i.priority = ServicePriority::Core;
return i;
}
};
/**
* @brief 控制台日志服务
*/
class ConsoleLogger : public ILogger {
public:
ConsoleLogger();
~ConsoleLogger() override;
bool init() override;
void shutdown() override;
void level(LogLevel lvl) override;
LogLevel level() const override;
bool enabled(LogLevel lvl) const override;
void log(LogLevel lvl, const char *fmt, ...) override;
void log(LogLevel lvl, const std::string &msg) override;
void trace(const char *fmt, ...) override;
void debug(const char *fmt, ...) override;
void info(const char *fmt, ...) override;
void warn(const char *fmt, ...) override;
void error(const char *fmt, ...) override;
void fatal(const char *fmt, ...) override;
void levelColor(LogLevel lvl, const LogColor &c) override;
LogColor levelColor(LogLevel lvl) const override;
void colors(bool on) override;
bool colors() const override;
const char *levelString(LogLevel lvl);
std::string ansiColor(LogLevel lvl);
private:
void output(LogLevel lvl, const char *msg);
LogLevel level_ = LogLevel::Info;
bool colors_ = true;
LogColor levelColors_[7];
class Impl;
Unique<Impl> impl_;
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
};
} // namespace extra2d
// 格式化辅助
namespace extra2d {
namespace detail {
template <typename T> std::string to_string(T &&value) {
using D = std::decay_t<T>;
using Raw = std::remove_reference_t<T>;
if constexpr (std::is_same_v<D, std::string>)
return value;
else if constexpr (std::is_array_v<Raw>)
return std::string(value);
else if constexpr (std::is_same_v<D, const char *>)
return value ? value : "(null)";
else if constexpr (std::is_same_v<D, bool>)
return value ? "true" : "false";
else if constexpr (std::is_arithmetic_v<D>)
return std::to_string(value);
else
return "<?>";
}
inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
template <typename T, typename... Args>
void format_impl(std::string &result, const char *fmt, T &&value,
Args &&...args) {
while (*fmt) {
if (*fmt == '{' && *(fmt + 1) == '}') {
result += to_string(std::forward<T>(value));
return format_impl(result, fmt + 2, std::forward<Args>(args)...);
}
result += *fmt++;
}
result += " " + to_string(std::forward<T>(value));
format_impl(result, fmt, std::forward<Args>(args)...);
}
} // namespace detail
template <typename... Args>
std::string format_str(const char *fmt, Args &&...args) {
std::string result;
detail::format_impl(result, fmt, std::forward<Args>(args)...);
return result;
}
} // namespace extra2d
// 日志宏
#define E2D_LOG(lvl, ...) \
do { \
if (auto log = ::extra2d::ServiceLocator::instance() \
.tryGet<::extra2d::ILogger>()) \
if (log->enabled(lvl)) \
log->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
} while (0)
#define E2D_LOG_CAT(lvl, cat, ...) \
do { \
if (auto log = ::extra2d::ServiceLocator::instance() \
.tryGet<::extra2d::ILogger>()) \
if (log->enabled(lvl)) \
log->log(lvl, ::extra2d::format_str("[{}] {}", cat, __VA_ARGS__)); \
} while (0)
// 带类别的日志宏
#define E2D_TRACE(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Trace, cat, __VA_ARGS__)
#define E2D_DEBUG(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Debug, cat, __VA_ARGS__)
#define E2D_INFO(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Info, cat, __VA_ARGS__)
#define E2D_WARN(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Warn, cat, __VA_ARGS__)
#define E2D_ERROR(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Error, cat, __VA_ARGS__)
#define E2D_FATAL(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Fatal, cat, __VA_ARGS__)
// 常用日志类别
#define CAT_APP "应用"
#define CAT_WINDOWS "窗口"
#define CAT_RENDER "渲染"
#define CAT_SCENE "场景"
#define CAT_INPUT "输入"
#define CAT_AUDIO "音频"
#define CAT_ASSET "资源"
#define CAT_SYSTEM "系统"
#define CAT_DATA "存档"
#define CAT_MODULES "模块系统"
#define CAT_SERVICES "服务系统"