必须为每条日志自动注入协程id(cid)和请求id(correlation_id):第一步配置formatter启用上下文支持,第二步在中间件中set correlation_id,第三步自定义formatter从context和swoole获取并写入extra字段;同时需正确配置zipkin driver、endpoint_url、tracemiddleware及对应aspect以实现跨服务链路追踪。

面试官问你如何在 Hyperf 3.1 中实现协程 ID 追踪与链路日志分析,不是考你会不会装扩展,而是看你能不能让每条日志精准归属到具体协程、具体请求、具体调用路径——日志堆在一起时,你得能一眼揪出是哪个协程卡住了、哪次请求漏埋点了、哪个 Redis 调用没带上 trace_id。
给每条日志自动打上协程 ID 和请求 ID
第一步:确认 config/autoload/logger.php 中 formatter 已启用上下文支持。打开该文件,找到 'formatter' → 'constructor' 配置项,把第四个参数设为 true(启用 include_stacktraces),第五个参数也设为 true(允许换行)。这一步不改,协程上下文字段压根不会被序列化进日志。
第二步:在 HTTP 中间件里注入唯一 correlation_id。新建一个中间件,比如 app/Middleware/CorrelationIdMiddleware.php,在 handle() 方法开头写:\Hyperf\Context\Context::set('correlation_id', uniqid('req_', true));。注意别在控制器里 set,中间件才能保证所有子协程都能继承这个上下文。
第三步:修改日志 formatter,从上下文中取值并注入。创建自定义 formatter 类 app/Formatter/ContextLineFormatter.php,继承 Monolog\Formatter\LineFormatter,重写 format() 方法,在 $record['extra'] 里手动塞入 'correlation_id' => \Hyperf\Context\Context::get('correlation_id', 'unknown') 和 'cid' => \Swoole\Coroutine::getuid()。这一步漏掉,日志里就只有时间戳和消息,没有协程指纹。
用 Zipkin 实现跨服务链路追踪
方法一:配置 opentracing.php 启用 Zipkin driver
打开 config/autoload/opentracing.php,确保顶层有 'default' => env('TRACER_DRIVER', 'zipkin'),且 'enable' => ['guzzle' => true, 'redis' => true, 'db' => true] 全部设为 true。如果只开了 db 却关了 guzzle,HTTP 外部调用那一段链路就直接断开,UI 上显示成两个孤立的 Span。
方法二:校验 endpoint_url 和 timeout 是否生效
【endpoint_url 必须带完整路径 /api/v2/spans,少一个字符就 404】。例如填成 http://skywalking-oap:9411 是错的,正确写法是 http://skywalking-oap:9411/api/v2/spans。timeout 建议设为 1.0,设成 0.1 容易丢 span,且 tracer 不报错,问题极难发现。
方法三:确认 TraceMiddleware 和对应 Aspect 已启用
在 config/autoload/middlewares.php 中,确保 Hyperf\Tracer\Middleware\TraceMiddleware::class 已加入全局中间件数组;同时检查 vendor/hyperf/tracer/src/Aspect 下的 GuzzleAspect、RedisAspect、DbAspect 是否被自动加载——只要 opentracing.php 里 enable 对应项为 true,这些 Aspect 就会生效。缺任何一个,链路就在那里断掉,不是代码报错,而是数据根本不上报。
快速验证链路是否打通
① 启动服务后,curl 一次接口,比如 curl http://localhost:9501/api/test;
② 立刻查看 runtime/logs/hyperf.log,搜索关键词 correlation_id 和 cid,确认每行日志都含这两个字段,且值不为空、不重复;
③ 登录 SkyWalking UI,筛选服务名(必须与 opentracing.php 中 'app' => ['name' => env('APP_NAME')] 一致),看是否有完整调用拓扑,Span 列表里是否包含 http、redis、mysql 等节点;
④ 故意触发一次异常,比如在控制器里 throw new RuntimeException('test'),观察日志中是否自动记录 error.stack 和 error.message,并在 SkyWalking 的 Trace Detail 页看到红色标记的失败 Span。











