因为std::ofstream默认采用缓冲机制,日志先存内存不立即落盘,程序崩溃或未显式刷新时缓存数据丢失;spdlog若未配置flush_on()或全局刷新策略,也仅在logger析构时flush一次,同样可能导致末尾日志丢失。

为什么直接用 std::ofstream 写日志会丢数据?
因为默认缓冲机制:std::ofstream 通常采用全缓冲或行缓冲(取决于底层文件是否关联终端),写入内容先存内存,不立即落盘。程序崩溃、异常退出或未显式刷新时,缓存里还没写入的那几条日志就丢了。
常见现象:日志文件大小长时间不变,或重启后发现最后几十条记录消失。
- 用
file —— <code>std::endl会 flush,但性能差(每次写都触发磁盘 I/O) - 用
file —— 不自动 flush,必须手动调 <code>file.flush() - 设置无缓冲:
file.rdbuf()->pubsetbuf(nullptr, 0)—— C++ 标准不保证跨平台生效,Windows 下常被忽略
用 std::ofstream 实现可靠实时写入的三步法
核心是控制缓冲 + 显式同步,兼顾可靠性与可控性。
- 构造时加
std::ios::app模式,避免覆盖;加上std::ios::out和std::ios::ate可选(后者让指针初始在末尾) - 禁用流缓冲:
file.rdbuf()->pubsetbuf(nullptr, 0)在 Linux/macOS 有效,在 Windows 需配合_setmode(_fileno(file.native_handle()), _O_BINARY)才可能生效(不推荐强依赖) - 更稳妥的做法:保留默认缓冲,但每次写完立刻
file.flush();若还需确保落盘,再加fflush(file.native_handle())或fsync(_fileno(file.native_handle()))(Linux/macOS)/FlushFileBuffers(file.native_handle())(Windows)
示例关键片段:
std::ofstream log_file("app.log", std::ios::out | std::ios::app);
log_file & <unistd.h>)
</unistd.h>
多线程环境下写日志为何会乱序甚至崩溃?
std::ofstream 本身不是线程安全的 —— 多个线程共用同一个 std::ofstream 对象, 操作符调用内部缓冲、格式化、写入等步骤非原子,极易出现字符交错、部分写入、<code>badbit 置位等错误。
- 不要在多个线程中直接共享一个
std::ofstream对象 - 用互斥锁保护写操作:全局
std::mutex+std::lock_guard,但会成为性能瓶颈(尤其高频日志) - 更合理做法:每个线程写本地缓冲,由单独日志线程统一刷盘;或用无锁环形缓冲(如
boost::lockfree::spsc_queue)+ 单消费者模型 - 注意:
std::ofstream::is_open()和fail()检查必须在锁内做,否则判断结果可能失效
要不要用 syslog 或第三方库(如 spdlog)?
纯 C++ 标准库能做到实时写入,但工程中往往不值得从零实现健壮日志系统。
-
syslog(Linux/macOS):系统级服务,支持远程转发、轮转、优先级过滤,但 Windows 不原生支持,且无法精确控制“写入即落盘”的时机 -
spdlog:默认使用std::ofstream+flush_on策略,支持异步模式、每日/大小轮转、多 sink(文件 + 控制台 + syslog),其daily_logger_mt已内置线程安全和强制 flush 逻辑 - 如果项目已引入 CMake,
find_package(spdlog REQUIRED)几行就能启用带 sync 的文件 logger:
auto logger = spdlog::daily_logger_mt("app", "logs/app.log", 0, 0); // 每天 0 点轮转
logger->set_level(spdlog::level::info);
logger->flush_on(spdlog::level::info); // info 及以上级别自动 flush
logger->info("Application started");
真正容易被忽略的是:即使用了 spdlog,若没调 flush_on() 或没设 spdlog::cfg::set_default_logger() 后的全局 flush 级别,仍可能丢最后几条日志 —— 它默认只在 logger 析构时 flush 一次。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











