多进程直写同一日志文件必然导致内容损坏,根本解法是杜绝该行为:优先通过logger/syslog交由journald等系统服务统一处理;必须直写时用flock在cron层做实例互斥;高可靠场景应分离日志逻辑,由专用进程单线程落盘。

直接让多个定时任务实例同时往同一个日志文件里写内容,必然导致日志行被截断、拼接、覆盖甚至丢失——这不是概率问题,是确定性故障。核心解法只有一个:**杜绝多进程直写同一文件**。
优先走系统日志服务统一收口
别自己 fopen/fprintf。所有定时脚本改用 logger 命令或 syslog() 接口输出日志:
-
logger -t "mybackup" "Backup started at $(date)"→ 日志自动进入 rsyslog 或 journald - journald 天然支持多进程并发写入,按时间戳+进程ID自动排序,查日志用
journalctl -t mybackup - 好处是完全不用管锁、路径、轮转、权限——系统级服务已串行化落盘
必须直写文件时,用 flock 做脚本级互斥
不是给 write() 加锁,而是确保同一时刻最多一个脚本实例在运行、在写日志:
- crontab 中这样写:
* * * * * flock -n /tmp/backup.lock -c '/path/to/backup.sh' || exit 0 -
-n表示非阻塞,抢不到锁就跳过,避免任务堆积拖垮系统 - 锁文件必须用绝对路径,且所有同类任务共用同一个锁路径(如
/tmp/backup.lock) - 切勿在脚本内部调用 flock —— 异常退出易留死锁,应由 cron 层面控制
高可靠场景:分离日志写入逻辑
把“记日志”从主任务中抽出来,交给专用日志进程处理:
- 主脚本只往命名管道(
/tmp/myapp.log.fifo)或 Unix 域套接字发日志字符串 - 另起一个长期运行的守护进程(如用 Python 写),监听该管道,单线程顺序写文件
- 或接入 Fluentd/Logstash:脚本用
curl -X POST http://127.0.0.1:8888/logs发送,由它批量落盘 - 彻底规避多进程竞争,还能顺便做格式标准化、字段提取、异步刷盘
临时补救与避坑要点
如果短期无法重构,至少堵住几个高频雷区:
- 禁用
echo "msg" >> /var/log/app.log这类重定向——bash 的>>不是原子操作,多进程下极易撕裂一行 - 若坚持追加写,必须每次完整构造带换行符的整行字符串,再用
echo -n "$line" >> file,且配合外部 flock - 日志路径别放在
/tmp(可能被清理)或 NFS(flock 不可靠),优先选本地 ext4/xfs 分区 - 对关键任务,日志里显式记录 PID 和时间戳:
echo "[$(date -Isec)][$$] Backup done" >> log,便于事后交叉比对










