不能只用 error_log() 记录发货日志,因其缺乏订单号上下文、时间格式混乱、多进程写入错乱,且不支持事务绑定,易导致对账偏差;可靠方案是用数据库表 order_shipping_log 存储结构化日志,确保与订单强关联、事务一致、可检索。

为什么不能只用 error_log() 记录发货日志
直接调用 error_log() 写发货日志,看起来简单,但很快会出问题:日志没订单号上下文、时间格式混乱、多进程写入时内容错乱、查不到谁在什么时间发了哪笔货。PHP 默认日志不带事务绑定,发货成功但日志写失败,或日志写了但发货回滚,都会导致对账偏差。
真正可用的发货日志必须满足三点:与订单强关联、与数据库操作同事务(或至少可回溯)、字段结构化便于检索。
- 必须记录
order_id、shipping_no、logistics_company、operator_id、created_at - 日志写入动作应放在发货逻辑确认提交之后(如
$pdo->commit()后),或使用数据库表+触发器兜底 - 避免用
file_put_contents()追加到公共文件——并发高时易丢行、无原子性
用数据库表记录发货日志最稳妥
建一张 order_shipping_log 表,比文件日志更可靠,能和订单表做 JOIN 查询,也方便加索引查异常单。字段不必复杂,但 order_id 和 status 是关键。
CREATE TABLE `order_shipping_log` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `order_id` BIGINT UNSIGNED NOT NULL, `shipping_no` VARCHAR(64) DEFAULT '', `logistics_company` VARCHAR(32) DEFAULT '', `operator_id` INT UNSIGNED NOT NULL, `remark` TEXT, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 插入日志前,确保
order_id已存在且状态合法(比如是'paid'或'confirmed') - 不要在事务里 INSERT 日志再 COMMIT —— 若后续发货失败回滚,日志却已落库,造成脏数据;应在发货成功 COMMIT 后立即 INSERT
- 如果用 Laravel,可用
DB::transaction()包裹发货逻辑,再单独DB::table('order_shipping_log')->insert(...)
用 Monolog 写文件日志要绕开三个坑
若业务强制要求写文件(如审计合规),Monolog 是比裸写文件更可控的选择,但默认配置容易翻车。
立即学习“PHP免费学习笔记(深入)”;
- 别用
StreamHandler直接写php://stdout或未加锁的普通文件——并发下日志行会粘连 - 必须用
RotatingFileHandler并设置maxFiles=30,否则日志无限增长,ls -l都卡住 - Formatter 必须自定义,把
order_id、shipping_no注入上下文,否则 grep 时找不到目标单:$logger->info('发货完成', [ 'order_id' => $order->id, 'shipping_no' => $trackingNo, 'logistics' => $company, 'operator' => $adminId ]);
发货日志和订单状态更新必须有因果顺序
这是最容易被忽略的逻辑断点。很多系统先更新订单表 status = 'shipped',再写日志,看似合理,但如果写日志时抛出异常(如磁盘满、权限不足),订单已变更为“已发货”,但无据可查——财务或客服查不到谁、何时、用哪家快递发的货。
正确做法只有两种:
- 数据库方案:发货状态变更和日志插入,在同一事务中完成(推荐用存储过程或应用层显式事务控制)
- 补偿方案:先写日志(状态为
'pending'),再更新订单;若订单更新失败,则异步任务扫描status = 'pending'的日志,重试或告警 - 绝对不要依赖“先改状态,再写日志”这种弱一致性链路
真实线上环境里,磁盘满、MySQL 主从延迟、ORM 自动 commit 模式切换,都可能让“写完日志再改状态”变成伪命题。因果顺序不是编码风格问题,是资金和履约安全的底线。











