C++高性能异步日志库需实现线程安全、异步写入、分级控制、格式可配与低开销缓冲:采用无锁队列+后台线程批量落盘;栈上格式化或延迟格式化避免堆分配;预分配文件+writev/mmap优化IO;宏封装实现编译期级别过滤与零成本抽象。

用 C++ 实现一个简单但实用的日志库,核心是:线程安全、异步写入、分级控制、格式可配、零拷贝或低开销缓冲。高性能异步日志系统不是堆功能,而是做减法——避开锁、减少内存分配、避免 IO 阻塞主线程。
1. 异步写入:用无锁队列 + 后台线程
主线程不直接写文件,只把日志消息 push 到一个生产者-消费者队列;后台线程循环 pop 并批量落盘。
- 推荐用 boost::lockfree::queue 或自己实现的 SPSC(单生产单消费)无锁队列,避免 mutex 竞争
- 日志消息建议封装为固定大小结构体(如 256 字节),含 level、时间戳、线程 ID、长度、内容缓冲区,避免 new/delete
- 后台线程用
std::this_thread::sleep_for(1ms)+ 检查队列,或用条件变量唤醒(更省 CPU)
2. 日志格式与缓冲:栈上格式化 + 写时编码
不要在日志调用点拼接字符串(如 log_info("user=" + name + ", id=" + std::to_string(id))),这会触发临时对象构造和堆分配。
- 采用类似 spdlog 的 延迟格式化:记录原始参数(int/const char*/std::string_view),格式化推迟到后台线程中执行
- 或使用 栈上缓冲 + snprintf:每个日志消息预分配 512 字节栈空间,用
fmt::format_to_n或自研轻量格式器写入,避免动态增长 - 时间戳建议用
clock_gettime(CLOCK_MONOTONIC, ...)(Linux)或GetTickCount64()(Windows),比std::chrono构造开销更低
3. 文件写入优化:预分配 + 内存映射 or writev
避免每条日志都 write() 一次,也别用 std::ofstream(带流缓冲但不可控、异常开销大)。
立即学习“C++免费学习笔记(深入)”;
- 后台线程累积多条日志后,用
writev()一次性提交(Linux)或WriteFile()+ 重叠 IO(Windows) - 日志文件提前
ftruncate()预分配空间(如 1GB),配合mmap()实现“写内存即写磁盘”,再定期msync() - 滚动策略:按大小轮转(如 256MB),用原子整数计数当前文件已写字节数,满则 close + rename + open 新文件
4. 接口设计:够用、直观、零成本抽象
对外暴露极简 API,宏封装隐藏细节,编译期过滤级别,避免运行时判断。
- 定义
LOG_INFO("msg {}", var)这类宏,内部展开为:if (level >= INFO) logger->log(INFO, __FILE__, __LINE__, fmt_str, ##__VA_ARGS__) - 级别检查放在宏里,DEBUG 日志在
NDEBUG下完全不编译,无任何运行时开销 - 支持运行时动态调整全局级别(通过原子变量),不影响已有日志路径性能
- 不提供“日志上下文”“MDC”等 Java 风功能——C++ 项目通常靠 RAII 和显式传参管理上下文
基本上就这些。真正难的不是代码量,是压测时发现的隐性瓶颈:比如频繁 gettimeofday、std::string 隐式构造、日志队列假共享、文件系统 page cache 压力。上线前务必用 perf record -e cycles,instructions,cache-misses 对比打点。不复杂但容易忽略。











