Composer本身不提供链路追踪能力,它只是依赖管理工具,不参与请求生命周期;真正起作用的是通过Composer安装的OpenTelemetry等运行时SDK,需在应用初始化阶段手动集成并确保上下文透传。

Composer 本身不提供分布式链路追踪能力,也没有官方 tracing 或 opentelemetry 扩展包;直接在 Composer 生态中“基于 Composer 库”实现链路追踪,本质是选型和集成问题,不是开发新 Composer 插件。
为什么不能把 Composer 当成链路追踪的运行时载体
Composer 是 PHP 的依赖管理工具,核心职责是下载、安装、自动加载类库,它不参与 HTTP 请求生命周期、不启动服务、不拦截函数调用。链路追踪必须在应用运行时(如 Web 请求、CLI 命令、队列任务)中注入 span、传递 trace_id、上报数据——这些只能由应用框架或中间件完成。
-
composer install运行完就退出,无法持续采集请求链路 - Composer 的
autoload.php只负责类加载,不提供钩子拦截控制器、数据库查询或 RPC 调用 - 所谓“基于 Composer 库”,实际是指通过 Composer 安装追踪 SDK(如
open-telemetry/sdk),而非改造 Composer 自身
PHP 链路追踪该依赖哪些 Composer 包
真正起作用的是运行时 SDK 和导出器,它们通过 Composer 安装,但需手动接入。主流选择是 OpenTelemetry PHP SDK,目前由社区维护(open-telemetry/opentelemetry),尚未进 PSR 标准,但兼容 OTLP 协议。
- 必需:
open-telemetry/opentelemetry(核心 SDK) - 导出推荐:
open-telemetry/exporter-otlp(发往 Jaeger/Tempo/OTel Collector) - 自动插件(可选):
open-telemetry/instrumentation-guzzle、open-telemetry/instrumentation-pdo—— 它们依赖运行时 hook 机制,不是 Composer 插件,而是通过register_shutdown_function或包装器注入 - 避免使用已废弃的
jaeger-client-php或zipkin-php,它们不支持 W3C Trace Context 标准,跨语言联调易断链
如何让 Laravel/Symfony/FPM 应用真正上报 trace 数据
关键不在 Composer install,而在初始化时机和全局拦截点。例如 Laravel 中不能只在 ServiceProvider::boot() 初始化 tracer,必须确保在第一个请求进入前完成注册,否则中间件、DB 查询、HTTP 客户端调用无法被自动捕获。
- 在
public/index.php顶部(require __DIR__.'/../vendor/autoload.php';之后、new Kernel之前)调用GlobalTracer::set()和TraceProvider::setDefault() - 对 PDO 操作生效,需用
PDOInstrumentation::register(),且必须在所有new PDO()实例化前执行 - FPM 场景下,每个请求是独立进程,
register_shutdown_function可用于 flush span,但要注意超时导致上报丢失 —— 建议启用异步 exporter(如OtlpHttpExporter的async模式) - 别依赖
composer.json的autoload.files自动加载 tracer 初始化代码:它在 autoload 阶段执行,早于请求上下文,无法访问$_SERVER或路由信息,trace_id 生成会错乱
最容易被忽略的三个断裂点
链路在 PHP 里断掉,往往不是 SDK 没装好,而是标准未对齐或上下文丢了。
- HTTP header 透传用
traceparent,不是X-B3-TraceId:旧版 Zipkin 头在跨 Go/Python 服务时会被忽略,OpenTelemetry 默认只读traceparent - CLI 命令(如队列消费者)必须手动创建 root span:没有 HTTP 入口,
TraceContextPropagator不会自动提取,得用Span::startRootSpan() - 多线程/协程不安全:PHP-FPM 是多进程,没问题;但若用 Swoole,
GlobalTracer是进程级单例,需配合Swoole\Coroutine\run+Context手动绑定 span 到协程,否则 trace_id 在协程切换后丢失


















