推荐用std::localtime配合std::put_time生成日期文件名:先将std::chrono::system_clock::now()转为time_t,再转std::tm,最后用"%Y%m%d_%H%M%S"格式化;多线程需用localtime_r或localtime_s避免竞态。

用 std::chrono + std::put_time 生成带日期的文件名
Windows 和 Linux 下都得靠系统时间 API,但 C++11 起 std::chrono 配合 std::put_time 是最轻量、无依赖的方案。别用 time() + localtime() 手动拼字符串——容易出时区错、线程不安全,而且 Windows 下 localtime 还要加 _s 后缀。
关键点:必须用 std::gmtime 或 std::localtime 把 time_t 转成 std::tm,再喂给 std::put_time;直接格式化 std::chrono::system_clock::now() 不行。
- 推荐用
std::localtime(反映用户时区),除非你明确要 UTC 时间 - 注意
std::localtime返回的是静态缓冲区指针,多线程下必须加锁或改用localtime_r(Linux)或localtime_s(Windows) - 格式串建议用
"%Y%m%d_%H%M%S",避免冒号(:)和空格,Windows 文件名不支持
log_20240520_143022.txt
- 微秒部分用
std::chrono::duration_cast<:chrono::microseconds>(now.time_since_epoch()).count() % 1000000提取,补零到6位 - 进程 ID 可用
std::getpid()(POSIX)或GetCurrentProcessId()(Windows),避免多进程同时写同名日志 - 如果单进程多线程写同一目录,再加个线程局部计数器(
thread_local static int seq{0}),每次递增
组合后形如:log_20240520_143022_123456_p1234_t2.txt(末尾 p 是 pid,t 是 thread-local 序号)
Windows 下 std::put_time 编译失败或输出乱码
VC++ 2015–2019 默认不支持 std::put_time 的宽字符版本,且 locale 设置不生效会导致空输出或崩溃——这不是 bug,是 MSVC 的实现限制。
绕过方法只有两个:要么强制用窄字符流 + std::locale::classic(),要么换用更底层的 strftime。
- 加
std::cout.imbue(std::locale::classic());对所有std::put_time调用生效 - 更稳妥:用
strftime替代std::put_time,它在 Windows 下稳定得多 - 示例:
char buf[64]; strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S", std::localtime(&t));
日志目录不存在时直接写入会静默失败
std::ofstream 构造时路径含不存在的目录(比如 "logs/log_20240520.txt"),不会自动创建 logs/,而是让流处于 failbit 状态——不检查 .is_open() 就直接写,日志就丢了,还毫无提示。
- 务必在打开前调用
std::filesystem::create_directories("logs")(C++17) - 若不能用 C++17,Windows 用
CreateDirectoryA,Linux 用mkdir -p包装或mkdir+errno == EEXIST判断 - 检查流状态:
if (!file.is_open()) { /* 记录错误到 stderr 或 fallback 路径 */ }
这个坑藏得深:本地测试时目录可能已有,CI 或新部署环境一跑就失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











