

新闻资讯
技术学院不能只用 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')DB::transaction() 包裹发货逻辑,再单独 DB::table('order_shipping_log')->insert(...)
若业务强制要求写文件(如审计合规),Monolog 是比裸写文件更可控的选择,但默认配置容易翻车。
StreamHandler 直接写 php://stdout 或未加锁的普通文件——并发下日志行会粘连RotatingFileHandler 并设置 maxFiles=30,否则日志无限增长,ls -l 都卡住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 模式切换,都可能让“写完日志再改状态”变成伪命题。因果顺序不是编码风格问题,是资金和履约安全的底线。