核心是每次写日志前检查文件大小是否超限,超限时先关闭当前文件、倒序重命名旧文件(如.log→.1、.1→.2)、再新建空白日志文件,全程需互斥锁保障多线程安全。

日志文件达到大小阈值后如何自动切分
核心是判断当前日志文件是否超过设定上限,再重命名旧文件、新建空白日志。关键不在“写”,而在“切”——fopen 用 "a" 模式打开时,不能直接 rename 当前正在写入的文件(Windows 下会失败,Linux 虽允许但有风险)。正确做法是:先 close 当前 FILE*,再 rename,最后用 fopen(..., "w") 新建。
常见错误是边写边 rename,导致部分日志丢失或 EBUSY 错误。建议封装一个 rotate_if_needed() 函数,在每次写日志前检查:
if (current_size > max_size) {
fclose(log_fp);
rename("app.log", "app.log.1");
log_fp = fopen("app.log", "w");
current_size = 0;
}
注意:rename() 不是原子操作,若进程崩溃在 rename 后、fopen 前,下次启动会丢失日志;生产环境建议加临时标记文件或使用 link()+unlink() 组合提升可靠性。
如何限制保留最多 N 个历史日志文件
翻滚时要清理过期备份,否则磁盘迟早打满。典型策略是:把 app.log.N 逐级递增,从最大编号开始删,再整体 +1,最后生成新 .1。比如保留 3 个备份,现有 app.log.1、app.log.2,则先删 app.log.3(如果存在),再把 .2 → .3,.1 → .2,最后 app.log → .1。
容易踩的坑:
- 用
std::filesystem::remove_all()删除app.log.*通配 —— C++20 前不支持 glob,必须手动枚举 - 按字符串排序
app.log.10会排在app.log.2前面 —— 要提取数字后按整数比较 - 并发多进程写同一日志时,清理逻辑可能冲突 —— 单进程日志才适用此方案
简单实现可用 std::filesystem::directory_iterator 遍历当前目录,匹配 app.log.\d+ 正则(C++11 起支持),收集编号后排序处理。
时间驱动的翻滚(如每天零点新建日志)怎么安全触发
靠定时检查系统时间最稳妥,不要依赖 cron 或外部信号——C++ 进程自己得掌握节奏。每写一条日志都调用 time(nullptr) 开销小,但更高效的是记录上次翻滚时间戳,只在秒级变化时检查是否跨天。
关键点:
- 用
localtime_r()(POSIX)或localtime_s()(Windows)避免localtime()的线程不安全 - 比较
tm.tm_mday是否变化,而不是直接格式化字符串再 strcmp —— 避免时区切换导致误判 - 时间翻滚和大小翻滚可能同时发生,需确保二者互斥:比如先检查时间,再检查大小,且翻滚后重置两个状态
示例逻辑:
auto now = time(nullptr);
tm t;
#ifdef _WIN32
localtime_s(&t, &now);
#else
localtime_r(&now, &t);
#endif
if (t.tm_mday != last_rotate_day) {
// 执行翻滚...
last_rotate_day = t.tm_mday;
}
多线程环境下写日志时如何避免 rotate 冲突
rotate 操作涉及 close/rename/fopen 三步,必须原子化;而日志写入是高频操作。最轻量的解法是用一个 std::mutex 护住整个日志写入路径(包括 rotate 判断和实际 write),但会成为性能瓶颈。
更实用的做法是分离关注点:
- 写日志只操作内存 buffer(
std::string或 lock-free ring buffer) - 单独一个日志 flush 线程,定期(或 buffer 满时)持有 mutex 将 buffer 写入文件,并在此时做 rotate 判断
- rotate 完成后更新全局
FILE*指针,flush 线程后续写入新文件
注意:FILE* 是线程安全的(C 标准保证 fwrite 等函数内部加锁),但你不能假设它对 rename/fopen 这类操作也安全——所以 FILE* 的替换必须在临界区内完成,且所有写线程必须重新读取该指针(声明为 std::atomic<file></file> 或加 memory barrier)。
别忽略 SIGPIPE 或磁盘满时 fwrite 返回值为 0 的情况——此时应立即触发 rotate 并重试,否则日志静默丢失。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











