Skip to content

Copilot SDK 内置 OpenTelemetry 支持,通过 telemetry.otlpEndpoint 配置即可将 trace 数据发送到任何 OTLP 兼容的后端(Jaeger、Zipkin、OTEL Collector 等)。通过 onGetTraceContext 和 W3C Trace Context 传播,可以把 SDK、CLI 进程和自定义工具的 span 串联成完整的分布式追踪链。

GitHub Copilot SDK OpenTelemetry 可观测性:分布式追踪配置

基本配置

OpenTelemetry 追踪默认关闭,需要显式配置:

typescript
import { CopilotClient } from '@github/copilot-sdk'

const client = new CopilotClient({
  telemetry: {
    otlpEndpoint: 'http://localhost:4318'  // OTLP HTTP 端点
  }
})

Telemetry 配置选项

选项类型说明
otlpEndpointstringOTLP HTTP 端点 URL
filePathstringJSON Lines 格式的 trace 文件路径
exporterTypestring"otlp-http""file"
sourceNamestring插桩作用域名称(instrumentation scope)
captureContentboolean是否在 trace 中记录消息内容

导出到文件(本地调试)

typescript
const client = new CopilotClient({
  telemetry: {
    exporterType: 'file',
    filePath: '/tmp/copilot-traces.jsonl'
  }
})

跨进程 Trace Context 传播

SDK 运行在 Node.js 进程中,而 CLI 是独立的子进程。要把两边的 trace 串联起来,使用 onGetTraceContext 把 W3C Trace Context 注入到 CLI 请求中:

typescript
import { CopilotClient } from '@github/copilot-sdk'
import { propagation, context } from '@opentelemetry/api'

const client = new CopilotClient({
  telemetry: { otlpEndpoint: 'http://localhost:4318' },
  onGetTraceContext: () => {
    const carrier: Record<string, string> = {}
    propagation.inject(context.active(), carrier)
    // 返回 W3C 格式的 headers: { traceparent: "00-...", tracestate: "..." }
    return carrier
  }
})

在自定义工具中还原 Trace Context

当 CLI 调用你的自定义工具时,invocation 参数中包含 traceparenttracestate,可以用来还原 trace 上下文,让工具的 span 成为同一条 trace 的子节点:

typescript
import { propagation, context, trace } from '@opentelemetry/api'

session.registerTool(myTool, async (args, invocation) => {
  // 从 invocation 中提取 trace context
  const carrier = {
    traceparent: invocation.traceparent,
    tracestate: invocation.tracestate
  }
  
  // 还原父 span 的 context
  const parentCtx = propagation.extract(context.active(), carrier)
  const tracer = trace.getTracer('my-app')
  
  return context.with(parentCtx, () =>
    tracer.startActiveSpan('my-tool', async (span) => {
      try {
        const result = await doWork(args)
        span.setAttribute('result.size', result.length)
        return result
      } catch (error) {
        span.recordException(error as Error)
        throw error
      } finally {
        span.end()
      }
    })
  )
})

这样,整条请求链路(用户请求 → SDK → CLI → 工具调用)都会出现在同一个 trace 中,方便在 Jaeger/Zipkin/Tempo 中追踪完整路径。

配合 OTEL Collector

生产环境推荐部署 OpenTelemetry Collector 作为 trace 聚合点:

yaml
# otel-collector.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

exporters:
  jaeger:
    endpoint: jaeger:14250
  
  # 或者发到 Prometheus + Tempo
  otlp/tempo:
    endpoint: tempo:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [jaeger]

SDK 配置指向 Collector:

typescript
const client = new CopilotClient({
  telemetry: { otlpEndpoint: 'http://otel-collector:4318' }
})

隐私注意事项

captureContent: true 会把对话内容记录到 trace 中,方便调试但可能包含敏感信息。生产环境建议默认关闭,仅在开发调试时开启:

typescript
const client = new CopilotClient({
  telemetry: {
    otlpEndpoint: process.env.OTLP_ENDPOINT!,
    captureContent: process.env.NODE_ENV === 'development'
  }
})

常见问题

Q: 用 Jaeger All-in-One 本地调试怎么配置?

A: 运行 docker run -p 4318:4318 -p 16686:16686 jaegertracing/all-in-one,然后 otlpEndpoint: "http://localhost:4318",在 http://localhost:16686 查看 trace。

Q: SDK 默认会发送哪些 span?

A: 主要包括:createSessionsendPrompttool.call(每次工具调用)、CLI 通信的 JSON-RPC span。具体遵循 OpenTelemetry GenAI 语义约定。

Q: 不想用 OTLP,可以直接打印到控制台吗?

A: 用 exporterType: "file" 导出到文件,然后用 tail -f 监控。或者在 OTEL SDK 中注册 ConsoleSpanExporter 打印到 stdout——这是 OpenTelemetry 标准用法,SDK 层面不特殊处理。