可借助装饰器模式在不修改原有日志类前提下动态叠加功能,具体包括:一、继承+组合式装饰器;二、函数对象装饰器链;三、RAII式作用域装饰器;四、宏驱动的编译期装饰器注入。

如果您在C++项目中已存在一个基础异步日志组件,但需要在不修改原有日志类的前提下动态叠加功能(如日志级别过滤、上下文注入、格式化增强或远程上报),则可借助装饰器模式实现运行时可插拔的功能扩展。以下是实现该中间件增强的多种具体方法:
一、继承+组合式装饰器实现
通过定义抽象日志接口,并让每个装饰器同时继承该接口并持有一个被装饰的日志对象指针,实现职责链式调用。该方式类型安全、编译期绑定,便于静态分析。
1、定义抽象日志接口 ILogSink,含纯虚函数 log(const LogEntry& entry) 和 setNext(std::shared_ptr
2、实现基础异步日志类 AsyncFileSink,继承 ILogSink 并完成文件写入与线程队列投递;
3、编写装饰器类 LevelFilterDecorator,构造时接收 std::shared_ptr
4、按需串联装饰器:创建 AsyncFileSink 实例,传入 LevelFilterDecorator,再传入 ContextInjectorDecorator,最终调用顶层 log() 即触发完整链路。
二、函数对象装饰器链(std::function + 可变参数模板)
利用 C++17 的折叠表达式与类型擦除,将日志处理逻辑封装为可组合的 std::function
1、声明核心处理函数类型别名:using LogHandler = std::function
2、实现装饰器工厂函数 makeLevelFilter(LogHandler next, LogLevel min_level),内部返回一个 lambda,捕获 next 与 min_level,仅当满足级别条件时调用 next(entry);
3、实现上下文注入装饰器 makeContextInjector(LogHandler next, std::map<:string std::string> ctx),在调用 next 前将 ctx 合并至 entry.context;
4、组合调用:auto pipeline = makeContextInjector(makeLevelFilter(AsyncFileSink::handle, WARN), {{"service", "auth"}});,后续直接调用 pipeline(entry) 即完成全链路处理。
三、RAII 式作用域装饰器(基于栈对象生命周期)
针对临时性、局部化的功能增强需求(如某段业务代码块内强制启用 trace 级别日志),采用 RAII 将装饰行为绑定到作用域生命周期,避免手动管理链路状态。
1、定义装饰器句柄类 ScopedLogDecorator,构造时向全局日志注册表注册当前线程专属的装饰器链,析构时自动移除;
2、该类接受任意数量的装饰器构建器(如 LevelFilterBuilder(WARN)、TraceIdInjectorBuilder()),并在构造时完成链式装配;
3、在目标作用域起始处声明:ScopedLogDecorator guard{LevelFilterBuilder(DEBUG), TraceIdInjectorBuilder()};;
4、此后同一线程内所有 LOG_INFO 等宏发出的日志均自动经过该装饰链,离开作用域后恢复原始行为。
四、宏驱动的编译期装饰器注入
通过预处理器宏在日志宏展开阶段静态插入装饰逻辑,适用于对性能极度敏感且装饰策略固定不变的场景,无运行时开销,但牺牲灵活性。
1、重定义日志宏 LOG_DEBUG(fmt, ...),使其展开为 __log_with_decorators(DEBUG, fmt, ##__VA_ARGS__);
2、实现 __log_with_decorators 函数模板,模板参数包含装饰器类型列表(如 std::tuple
3、使用 std::apply 与折叠表达式依次调用各装饰器的 pre_process() 与 post_process();
4、在 CMakeLists.txt 中通过 -DENABLE_JSON_FORMATTER -DENABLE_METRICS_REPORT 控制宏开关,实现编译期功能裁剪。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











