线上Symfony命令异常需主动配置日志:明确通道与级别(如prod环境为console通道单独设debug级文件输出),结合-vvv/--no-ansi还原执行流,结构化记录trace_id与关键动作,按退出码和异常类型(ProcessFailedException/ProcessTimedOutException)分类处理。

线上命令执行异常,不能只看报错信息——得让日志说话。Symfony 命令的日志本身不自动记录完整上下文,必须主动配置、分层捕获、定向过滤,才能从海量输出中揪出真正的问题点。
明确日志通道与级别,避免信息淹没
默认情况下,console 命令使用 console 通道,但该通道在生产环境通常只输出 ERROR 级别以上内容。若想捕获调试线索,需显式启用更细粒度的日志:
- 在
config/packages/prod/monolog.yaml中为 console 通道单独配置 handler,例如写入独立文件:handlers: console_file: type: stream path: "%kernel.logs_dir%/console.log" level: debug channels: [console] - 确保命令中实际调用的是带上下文的 logger,比如:
$this->logger->info('User import started', ['batch_id' => $id, 'source' => 'csv']); - 避免在命令里混用
var_dump()或echo,它们绕过 Monolog,无法被集中采集和过滤
结合 -vvv 与 --no-ansi,还原原始执行流
线上命令常通过 crontab 或 supervisor 启动,终端不可见。此时 -vvv 不仅输出更多细节,还会强制开启 debug 级别内部日志(如容器服务解析、参数绑定过程),配合 --no-ansi 可消除颜色控制符干扰,便于日志解析:
- 示例调用:
php bin/console app:import-users --env=prod -vvv --no-ansi >> /var/log/myapp/import.log 2>&1 - 重点关注输出中类似
[debug] Resolved command "app:import-users" to class "App\Command\ImportUsersCommand"这类容器解析日志,可确认是否加载了预期的命令类 - 若命令卡住无输出,-vvv 下通常会在某一步骤停止,直接定位到阻塞点(如 Doctrine 连接池耗尽、Redis 超时)
结构化记录关键动作,支持事后回溯
纯文本日志难以关联同一任务的多次执行。应在命令生命周期关键节点注入结构化字段:
- 在
execute()开头生成唯一 trace_id:$traceId = uniqid('cmd-', true); $this->logger->info('Command started', ['trace_id' => $traceId]); - 数据库操作前后记录耗时与参数:
$this->logger->info('Fetching users', ['trace_id' => $traceId, 'limit' => 1000]);$this->logger->info('Users fetched', ['trace_id' => $traceId, 'count' => $users->count()]); - 这样可在 ELK 或 Grafana 中按
trace_id聚合全部日志,还原单次执行全貌
区分命令失败类型,针对性处理
不是所有“失败”都该报警。要根据退出码和日志特征分类响应:
-
ProcessFailedException 类错误(退出码非 0):检查日志末尾的
Output:和Error Output:块,重点看底层系统命令是否缺失(如convert未安装)、权限不足或磁盘满 -
超时类失败(ProcessTimedOutException):日志中会出现
Command timed out after 30 seconds,需结合top或strace查看当时 CPU/IO 状态 -
逻辑异常但退出码为 0:比如数据校验失败后仅写日志未 throw,此时依赖结构化日志中的
status: failed字段触发告警,而非 exit code


















