laravel队列日志需每行输出合法json且字段名固定,启用'json'=>true配置,通过jobprocessed/jobfailed事件统一注入上下文,避免自由文本和大对象,logstash中用json filter配合remove_field和date filter,并用tail+pq本地验证结构。

队列任务日志怎么打才能被ELK自动解析
默认的 Laravel 队列日志是纯文本,json_decode 会失败,Logstash 的 json 过滤器直接跳过——根本进不了 Elasticsearch。必须让每条日志本身是一行合法 JSON,且字段名固定、类型稳定。
- 别用
Log::info('task started')这类自由文本,它无法被结构化提取 - 改用
Log::channel('stack')->info('queue job executed', [...]),第二个参数传关联数组,Laravel 会自动转成 JSON 字段(前提是日志驱动是single或daily且json设为 true) - 确保
config/logging.php中对应 channel 的'json' => true已启用,否则即使传了数组,输出仍是 key=value 格式 - 字段名避免空格、点号、斜杠,比如用
job_name而非job.name,否则 Logstash 的grok或dissect容易出错
Laravel 队列监听时如何注入统一上下文
单个任务里硬编码 Log::info(..., ['job_id' => $job->jobId()]) 不现实——你得在每个 handle() 开头手动加,漏一个就断链。真正可行的是利用 Laravel 的 JobProcessed 和 JobFailed 事件,全局拦截。
- 在
App\Providers\EventServiceProvider的$listen中注册:Illuminate\Queue\Events\JobProcessed::class => [App\Listeners\LogQueueExecution::class] -
LogQueueExecution的handle()方法里调用Log::channel('queue_json')->info('job processed', [...]),把$event->job->resolveName()、$event->job->attempts()、耗时等全塞进去 - 注意:不要在监听器里抛异常,否则可能干扰队列重试逻辑;如需记录失败,单独监听
JobFailed事件 - 避免在日志中写入大对象(如整个
$job->data),容易撑爆 ES 字段长度限制(默认text类型是 32766 字节)
logstash 配置里最常踩的 JSON 解析坑
很多人配了 json { source => "message" } 却发现字段没展开,其实是因为日志文件里混了非 JSON 行(比如 PHP warning、Symfony debug header),Logstash 一遇到解析失败就整条丢弃。
- 先用
filebeat做预过滤:在filebeat.inputs中加exclude_lines: ['^\s*$', '^\[.*\].*'],跳过空行和非 JSON 开头的调试行 - Logstash 的
jsonfilter 必须配合remove_field => ["message"],否则原始字符串还在,Kibana 里会看到重复字段 - 如果日志里有嵌套 JSON 字符串(比如
"payload": "{\"user_id\":123}"),默认jsonfilter 不会递归解析,得用mutate + json_decode二次处理 - 时间戳字段名必须是
@timestamp,否则 Kibana 时间范围筛选失效;Laravel 默认用datetime,要在 Logstash 里用datefilter 显式映射
本地开发时怎么快速验证日志结构是否合格
别等部署到 ELK 环境才发现字段对不上——用 tail -f + jq 就能秒级验证。
- 运行
php artisan queue:work --verbose,同时开另一个终端:tail -f storage/logs/laravel.log | jq -r '.job_name // .message' - 如果报
parse error,说明某行不是合法 JSON;如果输出null,说明字段名不对或被嵌套了 - 检查关键字段是否存在:
tail -f storage/logs/laravel.log | jq 'has("job_name") and has("duration_ms") and has("@timestamp")' - 注意:本地
APP_ENV=local时,日志可能走stackchannel 并包含多行堆栈,务必切到APP_ENV=production测试,否则结构不一致
结构化日志最难的不是打点,而是字段生命周期管理——今天加了个 retry_reason,下周有人删了这个字段又不通知 Logstash 维护者,Kibana 里查出来的就是空值。上线前最好用 jq 抽样 100 条,确认所有字段都稳定存在且非空。











