Hyperf\Utils\Coroutine::id()是唯一可靠、线程安全的协程ID获取方式,因swoole_coroutine_id()返回底层原始ID,可能在嵌套协程或异常恢复时与Hyperf上下文不一致,导致日志追踪断裂;该函数须在协程内调用,否则返回0,推荐通过Monolog Processor统一注入cid字段以保障日志稳定性。

Hyperf\Utils\Coroutine::id() 是唯一可靠、线程安全的获取方式。其他如 swoole_coroutine_id() 在 Hyperf 环境下可能返回错误值,尤其在嵌套协程或异常恢复路径中。
为什么不能直接用 swoole_coroutine_id()
Hyperf 封装了 Swoole 协程,并做了上下文增强和错误恢复处理。swoole_coroutine_id() 返回的是底层 Swoole 的原始 ID,而 Hyperf 可能会复用或重映射协程上下文(比如在 defer、异常捕获后重建),此时两者不一致。实测中,swoole_coroutine_id() 在子协程中有时返回 0 或父协程 ID,造成日志追踪断裂。
Hyperf\Utils\Coroutine::id() 的使用场景
该函数必须在协程内调用,否则返回 0(表示“非协程上下文”)。常见误用点:
- 在控制器方法顶层(非
co/go内)直接调用 → 得到0 - 在
defer回调里调用 → 正常,仍属原协程生命周期 - 在
Context::set()后的子协程中调用 → 返回新协程 ID,不是父协程 ID
如何在日志中稳定打上协程 ID
推荐在日志处理器中统一注入,而不是每个 $logger->info() 手动拼接。例如:
// 自定义 Monolog Processor
return function (array $record) {
$record['context']['cid'] = \Hyperf\Utils\Coroutine::id();
$record['context']['trace_id'] = \Hyperf\Context\Context::get('trace_id') ?: 'unknown';
return $record;
};
这样所有日志自动带 cid 字段,无需业务代码反复写 Coroutine::id(),也避免漏写或写错位置。
协程 ID 不是全局唯一持久标识,它只在当前进程生命周期内有效,重启后重置;跨机器、跨进程时完全不可比。真正需要用户级追踪,请始终绑定 user_id 或 trace_id,而不是依赖 cid 做业务判断。











