thinkphp 6 定时任务需手动实现链路追踪:入口初始化 trace_id/span_id,command 中创建 root span 并存入容器;db/http 调用通过容器获取上下文自动埋点并注入 traceparent;上报采用 udp 或异步队列,采样按任务名白名单或哈希控制,拓扑数据结构化发送至 apm 后端。

ThinkPHP 6 本身不提供定时任务的链路追踪能力,所有 trace 数据必须靠你手动注入、透传和上报。定时任务(如 think task 或基于 crontab + php think command 触发的命令)是脱离 HTTP 请求上下文的独立执行流,没有 Request 对象、没有 header、也没有现成的 trace_id 可继承——这意味着你不能复用 Web 接口那套中间件逻辑,得单独设计一套轻量、可靠、可收敛的追踪机制。
定时任务的 trace 必须从入口就初始化,且全程自主管理生命周期
定时任务启动时没有任何外部上下文,所以 trace_id、span_id 都得自己生成,并显式传递到子调用中(比如 DB 查询、HTTP 调用、消息投递)。别指望框架自动帮你挂载或延续 trace,它根本不知道你在跑什么。
在 Command 类里统一初始化 root span
每个需要追踪的 Command(如 app/command/SyncOrder.php)应在 execute() 开头主动创建 root span,而不是等进到业务方法才动手。示例结构:
- 生成唯一 trace_id(推荐
uniqid('t-', true)) - 生成 span_id(可用
dechex(crc32($trace_id . microtime(true)))) - 设置基础标签:
component: 'command'、command: $this->getName()、env: config('app.env') - 记录开始时间戳(用于计算耗时)
- 将 span 上下文存入
Container::getInstance()->set('trace_context', $context)
这样后续任意位置(如模型回调、Db::listen、HttpHook)都能从容器取上下文,补全 child span。
DB 和 HTTP 调用要自动埋点,但需绕过请求上下文依赖
ThinkPHP 的 Db::listen() 和 think\Http 事件钩子本身不带 trace 上下文,你需要:
- 在
Db::listen()回调里,先尝试从容器读trace_context;若有,就新建 child span,标注db.statement、db.type、db.duration,并在finish()前记录错误信息 - 对
think\Http发起的远程调用,手动注入traceparent(W3C 格式)头:traceparent: 00-{trace_id}-{span_id}-01,下游服务若也支持 OpenTelemetry,就能自动续链 - 若调用的是无 trace 能力的旧服务,至少记录
http.url、http.status_code、http.duration到当前 span 的 tag 中
上报策略要适配 CLI 环境,避免阻塞主流程
CLI 下无法像 Web 那样靠响应结束触发上报,必须显式控制:
- 不要用同步 HTTP 上报(易超时卡住任务)
- 推荐 UDP 直连(如 Jaeger Agent 的
127.0.0.1:6831)或本地队列异步转储(如写入 Redis list,另起守护进程批量上报) -
finish()后立即调用$span->flush()或$tracer->forceFlush(),确保数据不滞留在内存 - 加异常兜底:
register_shutdown_function()里检查是否有未 finish 的 span,强制补日志并上报失败标记
采样逻辑要独立配置,不能和 Web 共用同一套规则
定时任务流量低但重要性高(比如财务对账、库存同步),建议:
- 默认 100% 采样,或按任务名白名单控制(如只采
SyncOrder和CloseMonth) - 若需降采样,用
$this->getName()做哈希:crc32($commandName) % 100 ,避免随机丢关键任务 - 采样率可存在 Redis,但 CLI 任务每次启动都应重读一次,不依赖运行时热更新(除非你有长驻进程)
拓扑染色与状态上报需结构化,不能只打日志
光写 Log::info("SyncOrder done") 没用。真正有用的链路数据包括:
-
service.name:thinkphp-command -
operation.name:SyncOrder -
status.code:0(成功)或1(失败) -
duration.ms: 实际执行毫秒数 -
error.message: 异常类名 + 简短提示(不带堆栈) -
host.ip: 执行机器内网 IP(gethostbyname(gethostname()))
这些字段要组装成 JSON,通过 HTTP/UDP 发给 SkyWalking OAP、Jaeger Collector 或自建聚合服务,才能出现在拓扑图上并支持健康度统计。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











