添加日志功能,包括异步支持、日志级别设置和日志格式配置
This commit is contained in:
104
src/core/logger.cpp
Normal file
104
src/core/logger.cpp
Normal 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
347
src/core/logger.h
Normal 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
|
||||
Reference in New Issue
Block a user