日志丢失因缓冲区未刷新:程序退出时内存缓冲区内容未写入磁盘;应显式调用flush()、启用unitbuf或封装日志函数统一刷新;多线程下operator

用 std::ofstream 写日志,为什么程序一退出就丢日志?
因为默认缓冲行为:std::ofstream 会把日志先攒在内存缓冲区里,等缓冲区满、文件关闭或显式刷新才真正写入磁盘。程序崩溃或直接退出时,缓冲区内容就丢了。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每次写完调用
log_stream.flush(),但频繁刷盘影响性能 - 构造
std::ofstream时加std::ios::unitbuf标志,让每次后自动 flush(适合调试期) - 更稳妥的做法是封装一个日志函数,在末尾统一加
flush(),而不是依赖析构——析构顺序不可控,尤其多线程下
多线程环境下直接 operator 到全局 <code>std::ofstream 会崩吗?
会。C++ 标准不保证 std::ofstream 的 operator 是线程安全的;多个线程同时写,可能交错、截断、甚至触发 <code>std::terminate(如内部 std::string 重分配冲突)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 必须加锁:用
std::mutex包住整个写入 + flush 过程,不要只锁 - 避免在锁内做耗时操作(比如格式化时间字符串),提前算好再进锁
- 别用
std::endl—— 它等于\n + flush,重复刷盘;改用\n,自己控制 flush 时机
想按天滚动日志,freopen 能不能用在 C++ std::ofstream 上?
不能。freopen 是 C 标准库函数,操作的是 FILE*;std::ofstream 底层虽然可能共享文件描述符,但标准未规定其与 C 流的绑定关系,强行混用会导致未定义行为(常见表现:新文件写不进、旧句柄泄漏、ofstream 报 failbit)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 滚动逻辑自己实现:每次写前检查当前文件名是否符合“今天”,不符合就
close()原流,用新路径open()新流 - 用
std::filesystem::last_write_time()查文件修改时间,比单纯依赖系统时间更可靠(防止程序长时间运行后系统时间被校准) - 重命名旧日志时加后缀(如
.2024-06-15.log),别直接覆盖,避免运维误删
为什么用 __FILE__ 和 __LINE__ 打印位置,但实际输出总是显示日志函数内部的行号?
因为宏展开发生在预处理阶段,如果你把 __FILE__/__LINE__ 写在日志函数体里,它们展开的就是那个函数所在的文件和行,不是调用处。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 定义带参数的宏,例如:
#define LOG(msg) logger.log(__FILE__, __LINE__, msg)
- 避免在宏里调用复杂函数(如
std::to_string),容易引发求值顺序问题;优先用可变参数模板或std::format(C++20)做类型安全拼接 - 如果用宏,记得加
do { ... } while(0)包裹,防止if (cond) LOG("x"); else ...类场景出错
日志系统最难的不是写进去,是确保它在崩溃前真写出去、在并发时不乱序、在滚动时不丢数据——这些地方没测到,上线后就只能靠日志本身来查日志为什么没出来。










