php原生日志不支持按天切分,需禁用log_errors并用set_error_handler捕获错误,结合date('y-m-d')动态生成文件路径,通过flock加锁和时区校准确保多进程下安全切分。

PHP 自带的 error_log 不支持按天切分,得自己接管
PHP 的 error_log 函数或 log_errors = On 配置默认只往一个文件里追加写,没有轮转逻辑。想按天切分,必须绕过它,改用自定义日志写入 + 文件名动态生成。常见错误是直接在 php.ini 里配 error_log = /var/log/php/error.log 就以为能自动切分——结果一个月后发现单个文件几百 MB,tail -f 卡死,grep 慢到怀疑人生。
真正可行的做法:禁用 PHP 原生日志(设 log_errors = Off),所有错误统一捕获后,由 PHP 脚本自己决定写到哪、怎么命名、是否压缩归档。
用 set_error_handler + date('Y-m-d') 控制日志路径
核心是把错误捕获和文件路径生成绑定在一起。每天第一次写日志时,生成当天的文件名;后续写入复用该路径,避免频繁调用 date()。
- 不要每次写都调用
date('Y-m-d'),而是在初始化时缓存当天日期字符串,比如$logDate = date('Y-m-d'); - 日志路径建议包含年月目录,如
/var/log/php/<code>date('Y/m')/$logDate.log,避免单目录下几万个小文件 - 务必用
fopen($path, 'a')追加写,别用'w',否则并发请求会互相覆盖 - 注意权限:PHP 进程用户(如
www-data)必须对目标目录有写权限,否则静默失败,错误日志反而没地方写
手动切分时小心 flock 和时区导致的跨天错乱
如果多个 PHP 进程(比如 FPM worker)同时判断“今天是不是新一天”,可能因微秒级时间差或时区配置不一致,导致两个进程都创建了同一天的新文件,或漏切。
立即学习“PHP免费学习笔记(深入)”;
- 切分动作不能只靠时间判断,要加文件锁:
flock($fp, LOCK_EX)写入前锁定当日日志文件句柄 - 确认
date_default_timezone_set()已设为业务所在时区(如'Asia/Shanghai'),否则date('Y-m-d')返回的是 UTC 时间,凌晨 0 点切分变成北京时间早上 8 点 - 不要依赖系统 cron 每天零点删旧日志——PHP 进程可能还在往昨天文件里写。应在写入前检查当前日期是否变化,变化则关闭旧句柄、打开新文件
简单可用的最小实现(含日期检查 + 安全写入)
以下代码片段可直接嵌入项目启动逻辑(如框架的 bootstrap 或独立日志类中):
$logDir = '/var/log/php/' . date('Y/m');
mkdir($logDir, 0755, true);
$logFile = $logDir . '/' . date('Y-m-d') . '.log';
$fp = fopen($logFile, 'a');
set_error_handler(function($level, $msg, $file, $line) use (&$fp, &$logFile) {
$nowDate = date('Y-m-d');
$todayFile = dirname($logFile) . '/' . $nowDate . '.log';
if ($todayFile !== $logFile) {
flock($fp, LOCK_UN);
fclose($fp);
$fp = fopen($todayFile, 'a');
$logFile = $todayFile;
}
$time = date('Y-m-d H:i:s');
$entry = "[$time] [E$level] $msg in $file:$line\n";
fwrite($fp, $entry);
});
这段代码没做异常兜底(比如 fopen 失败)、没加日志大小限制、也没归档压缩——这些不是“按天切分”的必要条件,而是后续运维需求。先让每天一个文件稳住,再考虑别的。
最常被忽略的一点:FPM 模式下,worker 进程常驻内存,$fp 和 $logFile 是进程级变量,不会跨请求重置。所以日期检查必须在 handler 内实时做,不能只在脚本开头算一次。











