spdlog按天滚动日志的核心是daily_logger_mt,自动基于系统时间触发滚动,无需手动判断;初始化时需确保目录存在、合理设置滚动时刻与时区,并注意异步模式下日志可能跨天写入。

spdlog按天滚动日志的核心配置是 daily_logger_mt
直接用 spdlog::daily_logger_mt 就能实现按天切分,不需要手写时间判断或轮询。它内部基于 std::chrono 比较当前时间和文件最后修改时间,每天首次写入时自动重命名旧文件、新建当日文件。
常见错误是误以为要自己调用 rotate_on_date 或监听系统时间——daily_logger_mt 已封装全部逻辑,只要初始化正确,后续所有 info()、warn() 调用都会自动路由到当天文件。
- 第一个参数是 logger 名(用于
spdlog::get()获取),必须唯一 - 第二个参数是日志文件路径(如
"logs/app.log"),spdlog会自动在后面追加日期后缀,如app.log.2024-06-15 - 第三、四个可选参数指定“几点触发滚动”:小时(0–23)、分钟(0–59),默认是
0, 0(即凌晨0点)
文件名和归档行为由 daily_file_sink_st / daily_file_sink_mt 控制
你实际拿到的 logger 底层绑定了一个 daily_file_sink_mt(多线程安全),它决定三件事:何时新建文件、旧文件怎么命名、最多保留多少天。
默认行为是:每天 0 点滚动,旧文件加 .YYYY-MM-DD 后缀,不自动删除历史文件。如果需要限制保留天数,得手动设置 set_pattern 之外的选项——但注意:daily_logger_mt 本身不提供 set_max_files 接口,必须从 sink 取出并 cast:
auto logger = spdlog::daily_logger_mt("app", "logs/app.log");
auto sink = std::static_pointer_cast<:sinks::daily_file_sink_mt>(logger->sink());
sink->set_max_files(30); // 只保留最近30天
</:sinks::daily_file_sink_mt>
⚠️ 容易踩坑:如果传入的路径带目录(如 "logs/app.log"),spdlog 不会自动创建 logs/ 目录;需提前调用 std::filesystem::create_directories("logs"),否则首次运行直接崩溃(抛 spdlog::spdlog_ex 异常,提示无法打开文件)。
时区问题会导致滚动时间偏移
daily_file_sink 完全依赖本地系统时钟,不做任何时区转换。如果你的服务器时区是 UTC,而业务要求按北京时间(UTC+8)每天早6点滚动,那么不能靠改系统时区——那样会影响整个服务。
可行做法是:把滚动时间设为 22, 0(即 UTC 时间 22:00 = 北京时间次日 6:00),或者更稳妥地,换用 rotating_logger_mt + 自定义 filename_calculator,但代价是失去内置日期判断逻辑,需自己实现 should_rotate。
- 绝大多数场景直接用默认
0, 0即可,运维统一管理服务器时区 - 跨时区部署时,建议日志文件名强制用 UTC 时间戳(比如用
rotating_logger_mt配合%Y%m%d_%Hpattern),避免语义混淆
异步模式下滚动行为不变,但要注意 flush 时机
用 spdlog::daily_logger_mt 默认是同步写入;若改用异步(spdlog::daily_logger_st 不支持异步,必须用 spdlog::async_logger + 手动挂 daily_file_sink_mt),滚动仍由 sink 触发,和 logger 类型无关。
但异步模式下,某天最后一行日志可能滞留在队列里没来得及刷盘,就到了滚动时间——这部分日志会被写进新文件,而非原定的当天文件。这不是 bug,是异步设计的必然现象。
- 如果对“严格按天归档”有强要求(如审计场景),不要用异步 logger
- 若必须异步,可在进程退出前调用
spdlog::shutdown(),它会阻塞等待队列清空并完成最后一次滚动 - 定期
logger->flush()不能替代shutdown(),因为 flush 不等待 sink 的滚动逻辑执行
date -s "tomorrow")验证滚动是否触发。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











