瀏覽代碼

优化异步日志

johnclot69 5 天之前
父節點
當前提交
d179ced706
共有 1 個文件被更改,包括 115 次插入42 次删除
  1. 115 42
      src/common/AsyncLogger.h

+ 115 - 42
src/common/AsyncLogger.h

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