日志必须结构化为JSON并显式包含trace_id字段,否则ELK/Loki无法提取、聚合或关联链路;Hyperf需改用JsonFormatter、添加TraceIdProcessor、确保协程上下文有值,并通过Filebeat采集且配置json.keys_under_root: true。

日志必须结构化,否则 ELK/Loki 查不到 trace_id
Hyperf 默认输出的是纯文本日志,直接扔给 ELK 或 Loki 后,trace_id 会混在整行字符串里,无法被字段提取、无法做聚合、无法关联链路。你得先确保每条日志是 JSON 格式,并显式包含 trace_id 字段。
关键动作不是“接 ELK”,而是“改日志输出格式”:
- 修改
config/autoload/logger.php,把formatter换成Monolog\Formatter\JsonFormatter - 加一个自定义 Processor(比如
App\Logger\TraceIdProcessor),在$record->extra里塞入trace_id、app、env等字段 - 确认
Hyperf\Context\Context::get('trace_id')在协程上下文里确实有值(需开启hyperf/tracer并配置中间件)
漏掉任意一项,你在 Kibana 里搜 trace_id: "xxx" 就是空结果。
Filebeat 是最稳的日志采集方式,别用 Monolog TCP 直推
Monolog 支持 SocketHandler 直连 Logstash,但生产环境极不推荐:连接不稳定、无重试、丢日志风险高、无法做缓冲。Filebeat 是专为日志传输设计的,自带背压、断点续传、JSON 解析、字段增强。
Hyperf 容器里只需把日志写到文件(如 runtime/logs/app.log),然后让 Filebeat 挂载该目录读取即可:
- Filebeat 配置中启用
json.keys_under_root: true,自动把 JSON 日志的 key 提升为顶级字段 - 用
processors.add_fields补充service: "order-service"这类静态标签 - 避免在 Filebeat 里做复杂正则解析——Hyperf 已经输出 JSON,再解析是重复劳动且易错
如果你用 Docker,Filebeat 必须以 hostPath 或 bind mount 方式挂载宿主机日志目录,不能只靠容器内路径。
Loki 集成更轻量,但查询语法和字段习惯完全不同
Loki 不索引日志内容,只索引 label(比如 {app="user-service", level="error"}),所以它快、省资源,但查法很不一样:你不能像 ES 那样写 message : "timeout",而要靠 label 过滤 + LogQL 正则匹配。
对接 Hyperf 的要点是:
- Filebeat 输出时,必须把关键维度作为 label 提交,例如:
app、env、level、trace_id(Loki 支持trace_id作为 label) - 不要依赖
message字段查全文——Loki 对它的处理是流式扫描,慢且不准;真正该查的是{app="pay-service"} |~ "Alipay.*fail" - 如果要用 Grafana 查 trace_id 关联日志,确保
trace_id是 label,而不是藏在 JSON 的message里,否则无法和 Tempo 关联
很多人卡在第一步:Filebeat 把 trace_id 当成普通日志内容塞进 message,结果 Loki 里根本没法按 trace_id 过滤,只能全文扫,失去意义。
跨服务 trace_id 对齐失败,90% 出在 HTTP Client 透传漏了
日志里有 trace_id 不代表能串起来。Hyperf 服务 A 调用服务 B,B 的日志 trace_id 必须和服务 A 发出请求时带的 header 一致,否则链路就断了。
常见断点:
-
Hyperf\HttpClient\Client默认不透传trace_id,必须手动加 middleware 或封装 client,在 request header 中设置trace-id - 下游服务(比如 Go 写的)没按 OpenTracing 规范读取
trace-idheader,或大小写不一致(Hyperf 发trace-id,对方读Trace-Id) - 异步任务(如
Hyperf\AsyncQueue)里丢失上下文,Context::get('trace_id')返回null,需要在投递前显式传递并重建
验证方法很简单:找一条报错日志,复制它的 trace_id,去 Kibana 或 Grafana Logs 里搜同 trace_id 的所有日志——如果只看到本服务的几条,说明透传断了。这个环节比 ELK 配置本身更容易出问题。











