|
@@ -1,73 +1,146 @@
|
|
|
#ifndef ASYNC_LOGGER_H
|
|
|
#define ASYNC_LOGGER_H
|
|
|
|
|
|
-// 定义 _WIN32_WINNT 以支持 Windows 10/11 API
|
|
|
+// 定义 _WIN32_WINNT 以支持 Windows 10/11 API(0x0A00 表示 Windows 10/11)
|
|
|
#ifndef _WIN32_WINNT
|
|
|
#define _WIN32_WINNT 0x0A00
|
|
|
#endif
|
|
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
#include <spdlog/async.h> // 异步日志支持
|
|
|
-#include <spdlog/sinks/stdout_color_sinks.h> // 彩色终端输出
|
|
|
+#include <spdlog/sinks/stdout_color_sinks.h> // 控制台彩色输出
|
|
|
+#include <spdlog/sinks/rotating_file_sink.h> // 滚动文件输出
|
|
|
#include <memory>
|
|
|
#include <chrono>
|
|
|
+#include <vector>
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
-// 为 Boost 类型提供自定义格式化器
|
|
|
+// 自定义阈值过滤器 sink:包装另一个 sink,仅当消息级别 >= threshold 时转发日志
|
|
|
+#include <spdlog/sinks/sink.h>
|
|
|
+
|
|
|
+namespace spdlog::sinks {
|
|
|
+
|
|
|
+template<typename Mutex>
|
|
|
+class threshold_filter_sink : public sink {
|
|
|
+public:
|
|
|
+ threshold_filter_sink(std::shared_ptr<sink> wrapped_sink, spdlog::level::level_enum threshold)
|
|
|
+ : wrapped_sink_(std::move(wrapped_sink)), threshold_(threshold) {}
|
|
|
+
|
|
|
+ void log(const spdlog::details::log_msg &msg) override {
|
|
|
+ if (msg.level >= threshold_) {
|
|
|
+ wrapped_sink_->log(msg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void flush() override {
|
|
|
+ wrapped_sink_->flush();
|
|
|
+ }
|
|
|
+
|
|
|
+ void set_pattern(const std::string &pattern) override {
|
|
|
+ wrapped_sink_->set_pattern(pattern);
|
|
|
+ }
|
|
|
+
|
|
|
+ void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override {
|
|
|
+ wrapped_sink_->set_formatter(std::move(sink_formatter));
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ std::shared_ptr<sink> wrapped_sink_;
|
|
|
+ spdlog::level::level_enum threshold_;
|
|
|
+};
|
|
|
+
|
|
|
+} // namespace spdlog::sinks
|
|
|
+
|
|
|
+// --------------------------------------------------------------------
|
|
|
+
|
|
|
+// --------------------------------------------------------------------
|
|
|
+// 为 Boost 类型提供自定义格式化器(示例:仅提供 boost::asio::ip::basic_endpoint 格式化支持)
|
|
|
#include <boost/asio/ip/basic_endpoint.hpp>
|
|
|
#include <boost/asio/ip/address.hpp>
|
|
|
-#include <boost/system/error_code.hpp>
|
|
|
-#include <boost/beast/core.hpp> // 包含 boost::beast::error_code
|
|
|
-
|
|
|
namespace fmt {
|
|
|
template<typename Protocol>
|
|
|
struct formatter<boost::asio::ip::basic_endpoint<Protocol>> {
|
|
|
- // 无需额外格式说明符
|
|
|
- constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
|
|
+ constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); }
|
|
|
template<typename FormatContext>
|
|
|
- auto format(const boost::asio::ip::basic_endpoint<Protocol>& ep, FormatContext& ctx) {
|
|
|
+ auto format(const boost::asio::ip::basic_endpoint<Protocol> &ep, FormatContext &ctx) {
|
|
|
return format_to(ctx.out(), "{}:{}", ep.address().to_string(), ep.port());
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
+// 配置是否采用溢出丢弃策略(默认为阻塞策略)
|
|
|
+//#define ENABLE_ASYNC_OVERFLOW_OVERRUN
|
|
|
+
|
|
|
namespace logger {
|
|
|
|
|
|
- inline void init_async_logger() {
|
|
|
- // 使用 spdlog 官方推荐方法初始化全局线程池(队列大小 8192,线程数 1)
|
|
|
- spdlog::init_thread_pool(8192, 1);
|
|
|
-
|
|
|
- // 创建终端输出的 sink,并设置日志格式
|
|
|
- // 格式说明:
|
|
|
- // %Y-%m-%d %H:%M:%S.%e => 时间戳(毫秒级精度)
|
|
|
- // [%5l] => 日志级别(固定宽度 5,如 [INFO ])
|
|
|
- // [%n] => logger 名称(本例设为 "main")
|
|
|
- // %v => 日志内容
|
|
|
- // 输出示例:
|
|
|
- // 2025-03-11 16:00:20.043 [INFO ] [main] com.incubator.game.GameServerStart - 正在启动游戏服务器...
|
|
|
- auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
|
|
- console_sink->set_pattern("%Y-%m-%d %H:%M:%S.%e [%5l] [%t] [%s %!:%#] %v");
|
|
|
-
|
|
|
- // 使用 spdlog::thread_pool() 获取全局线程池
|
|
|
- auto async_logger = std::make_shared<spdlog::async_logger>(
|
|
|
- "main", // logger 名称
|
|
|
- console_sink, // 输出 sink
|
|
|
- spdlog::thread_pool(), // 使用全局线程池
|
|
|
- spdlog::async_overflow_policy::block // 队列满时阻塞,保证日志不丢失
|
|
|
- );
|
|
|
-
|
|
|
- // 将异步 logger 设为默认 logger,并设置日志等级及自动刷新策略
|
|
|
- spdlog::set_default_logger(async_logger);
|
|
|
- spdlog::set_level(spdlog::level::info);
|
|
|
- spdlog::flush_every(std::chrono::seconds(3));
|
|
|
- }
|
|
|
+// 使用全局静态线程池,确保其生命周期与日志器一致
|
|
|
+inline std::shared_ptr<spdlog::details::thread_pool> thread_pool;
|
|
|
|
|
|
- // 在 main() 退出前手动调用,确保日志系统干净关闭
|
|
|
- inline void shutdown_logger() {
|
|
|
- spdlog::shutdown();
|
|
|
- spdlog::thread_pool().reset(); // 显式释放线程池
|
|
|
- }
|
|
|
+// 定义编译时常量日志格式,与 log4j2 类似
|
|
|
+constexpr const char *console_pattern = "%Y-%m-%d %H:%M:%S.%e [%-5l] [%t] %n - %v";
|
|
|
+constexpr const char *file_pattern = "%Y-%m-%d %H:%M:%S.%e [%-5l] [%t] %n - %v";
|
|
|
+
|
|
|
+// 初始化异步日志模块
|
|
|
+inline void init_async_logger() {
|
|
|
+ // 日志文件路径及滚动参数
|
|
|
+ const std::string log_home = "../logs/game";
|
|
|
+ const std::string general_log_file = log_home + "/log.log";
|
|
|
+ const std::string error_log_file = log_home + "/error.log";
|
|
|
+ // 最大文件大小 250 MB,最多保留 240 个文件
|
|
|
+ size_t max_size = 250 * 1024 * 1024;
|
|
|
+ size_t max_files = 240;
|
|
|
+
|
|
|
+ // 创建线程池:队列大小 8192,线程数 1
|
|
|
+ thread_pool = std::make_shared<spdlog::details::thread_pool>(8192, 1);
|
|
|
+
|
|
|
+ // 创建控制台 sink
|
|
|
+ auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
|
|
+ console_sink->set_pattern(console_pattern);
|
|
|
+
|
|
|
+ // 创建普通日志文件 sink(滚动文件方式)
|
|
|
+ auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(general_log_file, max_size, max_files);
|
|
|
+ file_sink->set_pattern(file_pattern);
|
|
|
+
|
|
|
+ // 创建错误日志文件 sink(滚动文件方式)
|
|
|
+ auto error_file_sink_raw = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(error_log_file, max_size, max_files);
|
|
|
+ error_file_sink_raw->set_pattern(file_pattern);
|
|
|
+ // 使用阈值过滤器包装,仅记录 ERROR 及以上级别日志
|
|
|
+ auto error_file_sink = std::make_shared<spdlog::sinks::threshold_filter_sink<std::mutex>>(error_file_sink_raw, spdlog::level::err);
|
|
|
+
|
|
|
+ // 汇总所有 sink
|
|
|
+ std::vector<spdlog::sink_ptr> sinks { console_sink, file_sink, error_file_sink };
|
|
|
+
|
|
|
+#ifdef ENABLE_ASYNC_OVERFLOW_OVERRUN
|
|
|
+ constexpr auto overflow_policy = spdlog::async_overflow_policy::overrun;
|
|
|
+#else
|
|
|
+ constexpr auto overflow_policy = spdlog::async_overflow_policy::block;
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 创建异步 logger,名称设为 "logger"
|
|
|
+ auto async_logger = std::make_shared<spdlog::async_logger>(
|
|
|
+ "",
|
|
|
+ sinks.begin(), sinks.end(),
|
|
|
+ thread_pool,
|
|
|
+ overflow_policy
|
|
|
+ );
|
|
|
+
|
|
|
+ // 设置为默认 logger,设置全局日志级别为 DEBUG(文件输出用 DEBUG 级别,控制台同理,可根据需要调整)
|
|
|
+ spdlog::set_default_logger(async_logger);
|
|
|
+ spdlog::set_level(spdlog::level::debug);
|
|
|
+ // 针对错误日志,可以设置 flush_on(ERROR) 保证立即刷新
|
|
|
+ spdlog::flush_on(spdlog::level::err);
|
|
|
+ // 定时刷新所有日志(例如每 3 秒刷新一次)
|
|
|
+ spdlog::flush_every(std::chrono::seconds(3));
|
|
|
+
|
|
|
+ spdlog::info("Async logger initialized (Console, Rotating File, Error Filter).");
|
|
|
+}
|
|
|
+
|
|
|
+// 在程序退出前调用,确保日志系统干净关闭
|
|
|
+inline void shutdown_logger() {
|
|
|
+ spdlog::shutdown();
|
|
|
+ thread_pool.reset();
|
|
|
+}
|
|
|
|
|
|
} // namespace logger
|
|
|
|