Boost.Log连续重复日志处理:实现重复消息计数与跳过输出
实现Boost.Log重复日志消息的自动计数跳过功能
嘿,我之前封装Boost.Log日志库的时候也碰到过一模一样的需求——就是不想让重复日志刷屏,像syslog那样自动计数跳过。Boost.Log本身没有内置这个功能,但咱们可以通过自定义sink前端来实现,完全兼容你现有的Text File Backend和已配置的TimeStamp、Filter功能。
核心思路
咱们要在日志消息到达文件后端之前加个"中间层":
- 缓存最近一次的日志消息内容
- 遇到重复消息时只计数,不直接写入文件
- 遇到新消息时,先输出之前的重复计数提示,再写入新消息
- 程序退出时要处理剩余的重复计数,避免丢失最后一批的提示
具体实现步骤
1. 编写重复消息跟踪类
这个类负责缓存消息、计数,以及生成跳过提示:
#include <boost/log/sinks.hpp> #include <string> #include <mutex> #include <boost/log/expressions.hpp> #include <boost/log/core.hpp> class DuplicateMessageTracker { private: std::mutex m_mutex; std::string m_last_msg; size_t m_dup_count = 0; boost::log::sinks::sink_ptr m_target_sink; // 输出重复跳过提示的辅助函数 void emit_skip_notice() { if (m_dup_count > 0) { std::string notice = "Skipped " + std::to_string(m_dup_count) + " duplicate messages!"; // 构造日志记录并传给目标sink boost::log::record rec = boost::log::core::get()->open_record(); if (rec) { boost::log::record_ostream strm(rec); strm << notice; strm.flush(); m_target_sink->consume(rec); } m_dup_count = 0; } } public: explicit DuplicateMessageTracker(boost::log::sinks::sink_ptr target_sink) : m_target_sink(std::move(target_sink)) {} void process_record(const boost::log::record_view& rec) { std::lock_guard<std::mutex> lock(m_mutex); // 提取日志消息内容(这里要和你的日志属性名对应,默认是"Message") auto msg_opt = boost::log::extract<std::string>("Message", rec); if (!msg_opt) { // 如果提取不到消息,直接转发给目标sink m_target_sink->consume(rec); return; } std::string current_msg = *msg_opt; if (current_msg == m_last_msg) { // 重复消息,只计数 m_dup_count++; } else { // 新消息,先输出之前的跳过提示 emit_skip_notice(); // 转发当前消息到目标sink m_target_sink->consume(rec); // 更新缓存的消息 m_last_msg = std::move(current_msg); } } // 程序退出时调用,确保剩余的重复计数被输出 void final_flush() { std::lock_guard<std::mutex> lock(m_mutex); emit_skip_notice(); } };
2. 包装成Boost.Log兼容的Sink
Boost.Log的核心只认符合sink接口的对象,所以咱们把上面的跟踪类包装成标准sink:
class DuplicateFilterSink : public boost::log::sinks::sink { private: DuplicateMessageTracker m_tracker; public: explicit DuplicateFilterSink(boost::log::sinks::sink_ptr target_sink) : m_tracker(std::move(target_sink)) {} // 实现sink接口的必要方法 bool is_enabled() const override { return m_tracker.m_target_sink->is_enabled(); } void set_enabled(bool enabled) override { m_tracker.m_target_sink->set_enabled(enabled); } void consume(const boost::log::record_view& rec) override { m_tracker.process_record(rec); } void flush() override { m_tracker.final_flush(); m_tracker.m_target_sink->flush(); } };
3. 替换原有的File Sink
现在把你原本的Text File Sink用这个自定义sink包装起来,替换原来的添加逻辑:
// 1. 创建你原本的Text File Backend(保持你现有的配置) auto file_backend = boost::make_shared<boost::log::sinks::text_file_backend>( boost::log::keywords::file_name = "your_log_pattern.log", boost::log::keywords::rotation_size = 10*1024*1024, // 按大小切割 // 其他你原本的配置... ); // 2. 创建原始的同步sink前端 auto raw_file_sink = boost::make_shared<boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend>>(file_backend); // 3. 设置你原本的日志格式(保留TimeStamp、Filter这些) raw_file_sink->set_formatter( boost::log::expressions::stream << "[" << boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S") << "]" << " [" << boost::log::expressions::attr<std::string>("Filter") << "]" << " " << boost::log::expressions::smessage ); // 4. 用自定义的去重sink包装原始sink auto dedup_sink = boost::make_shared<DuplicateFilterSink>(raw_file_sink); // 5. 添加到日志核心(替换你原本的add_sink调用) boost::log::core::get()->add_sink(dedup_sink);
4. 程序退出时的收尾
一定要在程序结束前调用flush,确保最后一批重复消息的计数提示被输出:
// 比如在main函数末尾,或者全局对象的析构函数里 dedup_sink->flush();
关键注意事项
- 消息属性名:代码里的
"Message"是Boost.Log默认的消息属性名,如果你的ADD_LOG宏用了自定义的属性名(比如你自己定义了"LogMsg"),要修改extract<std::string>("Message", rec)里的字符串。 - 线程安全:用了
std::mutex保护共享状态,确保多线程日志场景下不会出问题。 - 动态消息处理:如果你的日志消息本身包含动态内容(比如把时间戳写在消息里),那这个逻辑会把它们当成不同的消息——不过你的TimeStamp是单独的属性,所以消息内容应该是固定的,没问题。
- 可扩展性:你可以轻松扩展这个类,比如添加"重复N次才开始跳过"的阈值,或者"相同消息间隔超过X秒就重新打印"的超时逻辑。
测试效果
当你调用三次ADD_LOG("Hello"),再调用一次ADD_LOG("Some Different Hello"),日志文件里会输出:
[2024-05-20 15:30:00] [YourFilterTag] Hello [2024-05-20 15:30:00] [YourFilterTag] Skipped 2 duplicate messages! [2024-05-20 15:30:00] [YourFilterTag] Some Different Hello
内容的提问来源于stack exchange,提问作者Jafar Gh
相关产品推荐
相关产品推荐

