CLI订单日志须用fopen或file_put_contents配合date动态生成带日期的文件名(如order_2024-06-15.log),追加写入并加LOCK_EX防并发错乱,内容含order_id、status、microtime(true),路径用__DIR__拼绝对路径。

CLI 模式下写订单日志必须用 fopen 配合 date 手动拼路径
Web 环境下常见的 error_log() 或 Laravel 的 Log::info() 在 CLI 中可能不生效,尤其当 PHP 配置了 log_errors = Off 或 error_log 指向了 Web 专用日志文件时。CLI 进程没有 Apache/Nginx 的日志上下文,得自己管文件打开、格式、权限和换行。
推荐做法是:每次写日志前用 date('Y-m-d') 动态生成带日期的文件名(如 order_2024-06-15.log),再用 fopen($file, 'a') 追加写入。避免用 'w' 模式,否则每次覆盖。
-
fopen()必须检查返回值,false表示目录不存在或无写权限,需提前mkdir -p或抛异常 - 日志行末尾务必加
\n,否则多条记录会挤在同一行 - 不要在循环里反复
fopen/fclose,高频写入建议打开一次、批量写入、最后关闭
订单日志内容至少包含 order_id、status、microtime(true)
单纯记时间不够,订单状态变更(如 created → paid → shipped)必须可追溯。Web 请求里能从 $_POST 或路由参数取 order_id,CLI 命令则要靠 $argv 传入,比如:php order_process.php --order-id=ORD-20240615-001 --status=paid。
用 microtime(true) 替代 date() 记毫秒级时间戳,方便排查并发或延迟问题;状态字段别只写中文,用英文标识符(pending/refunded)更利于后续 grep 或日志系统解析。
立即学习“PHP免费学习笔记(深入)”;
file_put_contents(
'/var/log/myapp/order_' . date('Y-m-d') . '.log',
sprintf(
"[%s] order_id=%s status=%s cost=%.3f ms\n",
date('Y-m-d H:i:s'),
$argv[2] ?? 'unknown',
$argv[4] ?? 'unknown',
(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000
),
FILE_APPEND | LOCK_EX
);
file_put_contents 加 LOCK_EX 是防止多进程日志错乱的关键
一个订单可能被多个 CLI 进程同时处理(如队列消费者),若不加锁,两行日志可能交叉写入,变成这样:[2024-06-15 10:00:00] order_id=ORD-1 status=paid[2024-06-15 10:00:01] order_id=ORD-2 status=shipped —— 中间缺换行,解析失败。
LOCK_EX 是排他锁,写入前阻塞其他进程,写完自动释放。注意:file_put_contents 的锁只对当前文件有效,不同日期的文件互不影响;但如果你用单个大日志文件(如 order_all.log),高并发下锁竞争会变严重,响应变慢。
- 不要用
flock()手动管理锁,file_put_contents的LOCK_EX更简洁可靠 - 如果日志量极大(每秒百条以上),考虑改用消息队列(如 Redis List + 后台写入线程),避免磁盘 I/O 成瓶颈
- PHP-FPM 和 CLI 共用同一份日志路径时,注意 umask 和目录属组,CLI 进程可能以
www-data外的用户运行,导致写权限不足
调试 CLI 日志时,先确认 getcwd() 和 __DIR__ 是否符合预期
CLI 下当前工作目录(getcwd())取决于你从哪执行命令,不是脚本所在目录。比如你在 /home/user 下运行 php /var/www/app/cli/order.php,getcwd() 是 /home/user,此时相对路径 ./logs/order.log 会写到错误位置。
安全做法是全部用绝对路径,通过 __DIR__ 定位脚本目录再拼接,例如:__DIR__ . '/../storage/logs/order_' . date('Y-m-d') . '.log'。上线前用 php -r "echo getcwd();" 和 php -r "echo __DIR__;" 对比验证。
另外,有些运维会禁用 allow_url_fopen,但这个设置不影响本地文件写入,别误判为配置问题。











