添加日志功能,包括异步支持、日志级别设置和日志格式配置

This commit is contained in:
2026-01-03 12:30:18 +08:00
parent 01815dfde5
commit 36e06f767b
2 changed files with 451 additions and 0 deletions

104
src/core/logger.cpp Normal file
View File

@@ -0,0 +1,104 @@
#include "logger.h"
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
mirai::logger::logger(logger_config config) : config_(config){
sinks_.clear();
if (config_.console_enabled) {
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(to_spdlog_level(config_.level));
sinks_.push_back(console_sink);
}
if (config_.file_enabled) {
auto log_dir = config_.file_path.parent_path();
if (!log_dir.empty() && !std::filesystem::exists(log_dir)) {
std::filesystem::create_directory(log_dir);
}
if (config_.file_rotation_enabled) {
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
config_.file_path.string(),
config_.max_file_size,
config_.max_files
);
file_sink->set_level(to_spdlog_level(config_.level));
sinks_.push_back(file_sink);
} else {
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(
config_.file_path.string(),
true
);
file_sink->set_level(to_spdlog_level(config_.level));
sinks_.push_back(file_sink);
}
}
if (config_.async_enabled) {
// 初始化异步日志线程池
spdlog::init_thread_pool(config_.async_queue_size, 1);
spdlog_logger_ = std::make_shared<spdlog::async_logger>(
config_.name,
sinks_.begin(),
sinks_.end(),
spdlog::thread_pool(),
spdlog::async_overflow_policy::block
);
} else {
spdlog_logger_ = std::make_shared<spdlog::logger>(
config_.name,
sinks_.begin(),
sinks_.end()
);
}
// 设置日志级别
spdlog_logger_->set_level(to_spdlog_level(config_.level));
// 设置日志格式
spdlog_logger_->set_pattern(config_.pattern);
// 设置刷新策略
spdlog_logger_->flush_on(spdlog::level::warn);
// 注册到 spdlog
spdlog::register_logger(spdlog_logger_);
}
mirai::logger::~logger() {
if (spdlog_logger_) {
spdlog_logger_->flush();
spdlog::drop(spdlog_logger_->name());
}
}
void mirai::logger::set_level(log_level level) {
config_.level = level;
if (spdlog_logger_) {
spdlog_logger_->set_level(to_spdlog_level(level));
}
for (auto& sink : sinks_) {
sink->set_level(to_spdlog_level(level));
}
}
void mirai::logger::set_pattern(const std::string& pattern) {
config_.pattern = pattern;
if (spdlog_logger_) {
spdlog_logger_->set_pattern(pattern);
}
}
bool mirai::logger::should_log(log_level level) const noexcept {
return spdlog_logger_ && spdlog_logger_->should_log(to_spdlog_level(level));
}
void mirai::logger::flush() {
if (spdlog_logger_) {
spdlog_logger_->flush();
}
}

347
src/core/logger.h Normal file
View File

@@ -0,0 +1,347 @@
#pragma once
#include <filesystem>
#include <source_location>
#include <string_view>
#include <spdlog/spdlog.h>
#include "types.h"
namespace mirai {
enum class log_level : u8 {
trace = 0,
debug = 1,
info = 2,
warn = 3,
error = 4,
critical = 5,
off = 6
};
[[nodiscard]] constexpr std::string_view log_level_to_string(log_level level) noexcept {
switch (level) {
case log_level::trace: return "trace";
case log_level::debug: return "debug";
case log_level::info: return "info";
case log_level::warn: return "warn";
case log_level::error: return "error";
case log_level::critical: return "critical";
case log_level::off: return "off";
default: return "unknown";
}
}
[[nodiscard]] constexpr spdlog::level::level_enum to_spdlog_level(log_level level) noexcept {
switch (level) {
case log_level::trace: return spdlog::level::trace;
case log_level::debug: return spdlog::level::debug;
case log_level::info: return spdlog::level::info;
case log_level::warn: return spdlog::level::warn;
case log_level::error: return spdlog::level::err;
case log_level::critical: return spdlog::level::critical;
case log_level::off: return spdlog::level::off;
default: return spdlog::level::info;
}
}
struct logger_config {
std::string name{"mirai"};
log_level level{log_level::info};
bool console_enabled{true};
bool file_enabled{true};
std::filesystem::path file_path{"logs/mirai.log"};
bool file_rotation_enabled{true};
size_type max_file_size{10 * 1024 * 1024}; // 10 MB
size_type max_files{5};
std::string pattern{"[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v"};
bool include_source_location{true};
bool async_enabled{false};
size_type async_queue_size{8192};
};
class logger {
public:
explicit logger(logger_config config = {});
~logger();
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
[[nodiscard]] const auto& name() const noexcept { return config_.name; }
[[nodiscard]] auto level() const noexcept { return config_.level; }
void set_level(log_level level);
void set_pattern(const std::string& pattern);
[[nodiscard]] bool should_log(log_level level) const noexcept;
void flush();
template <typename... Args>
void trace(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::trace, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void debug(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::debug, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void info(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::info, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::warn, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::error, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void critical(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::critical, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void log(log_level level, fmt::format_string<Args...> fmt, Args&&... args) {
if (should_log(level)) {
spdlog_logger_->log(to_spdlog_level(level), fmt, std::forward<Args>(args)...);
}
}
template <typename... Args>
void log_with_location(
log_level level,
const std::source_location& location,
fmt::format_string<Args...> fmt,
Args&&... args
) {
if (should_log(level)) {
std::string message = fmt::format(fmt, std::forward<Args>(args)...);
std::string full_message = fmt::format(
"{} [{}:{}:{}]",
message,
location.file_name(),
location.line(),
location.function_name()
);
spdlog_logger_->log(to_spdlog_level(level), "{}", full_message);
}
}
private:
logger_config config_;
std::shared_ptr<spdlog::logger> spdlog_logger_;
std::vector<spdlog::sink_ptr> sinks_;
};
inline auto& get_global_logger_ref() {
static std::shared_ptr<logger> instance;
return instance;
}
inline auto& get_global_logger_mutex() {
static std::mutex mutex;
return mutex;
}
inline void init_logger(const logger_config& config = logger_config{}) {
std::lock_guard lock(get_global_logger_mutex());
get_global_logger_ref() = std::make_shared<logger>(config);
}
inline void shutdown_logger() {
std::lock_guard lock(get_global_logger_mutex());
if (auto& logger = get_global_logger_ref()) {
logger->flush();
logger.reset();
}
spdlog::shutdown();
}
inline auto get_logger() {
std::lock_guard lock(get_global_logger_mutex());
auto& logger = get_global_logger_ref();
if (!logger) {
// 自动初始化默认日志器
logger = std::make_shared<mirai::logger>();}
return logger;
}
inline void set_log_level(log_level level) {
if (auto logger = get_logger()) {
logger->set_level(level);
}
}
inline void flush_logger() {
if (auto logger = get_logger()) {
logger->flush();
}
}
/**
* @brief 记录 trace 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_trace(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->trace(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 debug 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_debug(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->debug(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 info 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_info(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->info(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 warn 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_warn(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->warn(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 error 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_error(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->error(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 critical 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_critical(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->critical(fmt, std::forward<Args>(args)...);
}
}
}
/**
* @def MIRAI_LOG_TRACE(...)
* @brief 记录 trace 级别日志
*/
#define MIRAI_LOG_TRACE(...) ::mirai::log_trace(__VA_ARGS__)
/**
* @def MIRAI_LOG_DEBUG(...)
* @brief 记录 debug 级别日志
*/
#define MIRAI_LOG_DEBUG(...) ::mirai::log_debug(__VA_ARGS__)
/**
* @def MIRAI_LOG_INFO(...)
* @brief 记录 info 级别日志
*/
#define MIRAI_LOG_INFO(...) ::mirai::log_info(__VA_ARGS__)
/**
* @def MIRAI_LOG_WARN(...)
* @brief 记录 warn 级别日志
*/
#define MIRAI_LOG_WARN(...) ::mirai::log_warn(__VA_ARGS__)
/**
* @def MIRAI_LOG_ERROR(...)
* @brief 记录 error 级别日志
*/
#define MIRAI_LOG_ERROR(...) ::mirai::log_error(__VA_ARGS__)
/**
* @def MIRAI_LOG_CRITICAL(...)
* @brief 记录 critical 级别日志
*/
#define MIRAI_LOG_CRITICAL(...) ::mirai::log_critical(__VA_ARGS__)
/**
* @def MIRAI_LOG(level, ...)
* @brief 记录指定级别的日志
*/
#define MIRAI_LOG(level, ...) \
do { \
if (auto _logger = ::mirai::get_logger()) { \
_logger->log(level, __VA_ARGS__); \
} \
} while (false)
/**
* @def MIRAI_LOG_IF(condition, level, ...)
* @brief 条件日志记录
*/
#define MIRAI_LOG_IF(condition, level, ...) \
do { \
if (condition) { \
MIRAI_LOG(level, __VA_ARGS__); \
} \
} while (false)
// Debug 模式专用日志宏
#if MIRAI_DEBUG
/**
* @def MIRAI_DLOG_TRACE(...)
* @brief Debug 模式 trace 日志
*/
#define MIRAI_DLOG_TRACE(...) MIRAI_LOG_TRACE(__VA_ARGS__)
/**
* @def MIRAI_DLOG_DEBUG(...)
* @brief Debug 模式 debug 日志
*/
#define MIRAI_DLOG_DEBUG(...) MIRAI_LOG_DEBUG(__VA_ARGS__)
/**
* @def MIRAI_DLOG_INFO(...)
* @brief Debug 模式 info 日志
*/
#define MIRAI_DLOG_INFO(...) MIRAI_LOG_INFO(__VA_ARGS__)
#else
#define MIRAI_DLOG_TRACE(...) ((void)0)
#define MIRAI_DLOG_DEBUG(...) ((void)0)
#define MIRAI_DLOG_INFO(...) ((void)0)
#endif