Skip to content

OpenTelemetry

@vafast/opentelemetry 为 Vafast 提供 OpenTelemetry 追踪(tracing):给每个 HTTP 请求建根 span,并导出在业务代码里嵌套 span 的辅助函数。

本包以 trace / span 为主。NodeSDK 虽也可接 metrics / logs 相关选项,不等于本包已经提供现成的指标或日志中间件——完整可观测管线需你自行配置导出器与后端。

先搞清几个概念(给新用户)

Trace 与 Span

名词白话
Trace(追踪)一次完整请求链路的「故事」:从入口 HTTP 到数据库、下游 HTTP 等,用同一个 traceId 串起来
Span(跨度)故事里的一章:一段有起止时间的操作,例如整次 HTTP 请求、一次 SQL、一次缓存读取
属性(Attributes)贴在 span 上的键值信息,如 http.request.methodurl.path、业务自定义字段
上下文传播(Propagation)跨进程传递 trace:入站从 Headers 提取,出站再注入,才能把微服务画进同一条 trace

本中间件会为每个请求创建 SpanKind.SERVER 的根 span,名称最终更新为 `${method} ${pathname}`,并写入 method、path、status、部分请求/响应头等属性。

Preload SDK vs 中间件内启动

方式做什么适合
Preload(推荐)进程入口先 new NodeSDK(...).start(),再挂 opentelemetry()生产:OTLP 导出、采样、自动 instrumentation
仅中间件若全局仍是 ProxyTraceropentelemetry() 会用传入选项就地 new NodeSDK(...).start()本地快速试跑

已预加载且 tracer 不再是 ProxyTracer 时,中间件通常不会再启动一套 SDK,主要负责 HTTP 根 span 与属性。

instrumentations 是什么?

传给 NodeSDK 的自动埋点插件列表。例如 @opentelemetry/auto-instrumentations-node 可自动给 httpfs、部分数据库客户端等打 span,而不用你手写每一处。

typescript
instrumentations: [getNodeAutoInstrumentations()]

[] 或省略时:不会装这些自动插件;你仍能靠本中间件拿到 HTTP 根 span,并用 startActiveSpan 手写业务 span。

安装

bash
npm install @vafast/opentelemetry

按需再装导出器 / 自动埋点等(示例):

bash
npm install @opentelemetry/sdk-node \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/sdk-trace-node \
  @opentelemetry/auto-instrumentations-node

快速开始

typescript
import { Server, defineRoute, defineRoutes, serve } from 'vafast'
import { opentelemetry } from '@vafast/opentelemetry'

const routes = defineRoutes([
  defineRoute({
    method: 'GET',
    path: '/',
    handler: () => 'Hello, Vafast with OpenTelemetry!',
  }),
])

const server = new Server(routes)
server.use(
  opentelemetry({
    serviceName: 'example-app',
    instrumentations: [],
  }),
)

serve({ fetch: server.fetch, port: 3000 })

未预加载时,上述调用会在检测到 ProxyTracer 后启动 NodeSDK(serviceName + instrumentations + 其余透传选项)。

用法

推荐:预加载 SDK,再挂中间件

typescript
// preload.ts
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'

const sdk = new NodeSDK({
  serviceName: 'your-service',
  instrumentations: [getNodeAutoInstrumentations()],
  spanProcessors: [
    new BatchSpanProcessor(new OTLPTraceExporter()),
  ],
})

sdk.start()
typescript
import './preload'
import { Server, defineRoutes } from 'vafast'
import { opentelemetry } from '@vafast/opentelemetry'

const server = new Server(defineRoutes([/* ... */]))
server.use(opentelemetry({ serviceName: 'your-service' }))

业务内嵌 span

typescript
import {
  startActiveSpan,
  setAttributes,
  getCurrentSpan,
} from '@vafast/opentelemetry'

handler: async () => {
  return startActiveSpan('db.query-users', async (span) => {
    const rows = await db.query('select ...')
    span.setAttributes({ 'db.row_count': rows.length })
    setAttributes({ 'cache.hit': false }) // 写到当前活跃 span
    return { rows }
  })
}

getTracer().startActiveSpan / startActiveSpan / recordstartActiveSpan 别名)会在 Promise settle 或同步返回后自动 end();抛错时设置 ERRORrecordException

上下文传播

中间件用 propagation.extract 从入站 Headers 提取上游 trace context,便于跨服务串联。出站 HTTP/gRPC 需自行注入,或依赖 auto-instrumentations。

API

opentelemetry(options?)

typescript
opentelemetry(options?: VafastOpenTelemetryOptions): Middleware

VafastOpenTelemetryOptions = NodeSDK 构造参数 + 本包补充字段。下面列出初学者最常碰到的选项(其余以 @opentelemetry/sdk-node 为准):

参数说明
serviceName服务名;本包默认 '@vafast/vafast'。用于 tracer / SDK 标识
instrumentations自动埋点插件数组;见上文
traceExporter简易导出器(如 OTLP)。与 spanProcessors 二选一思路时,优先搞清 SDK 文档推荐写法
spanProcessorsSpan 处理器列表,常用 BatchSpanProcessor(exporter) 批量上报
sampler采样器:控制上报比例,高流量务必配置,避免成本爆炸
resource资源属性(服务名、环境、版本等)。部分项目用 Resource + semantic conventions 代替/补充 serviceName
resourceDetectors自动探测资源的 detector 列表
contextManager本包额外字段:若全局尚无 ContextManager,则 enable()setGlobalContextManager
textMapPropagator传播格式(如 W3C TraceContext);影响跨服务 Header
spanLimits单 span 属性/事件数量等上限
metricReader / views / logRecordProcessorsNodeSDK 支持,但本中间件主路径仍是 tracing

trace.getTracer(serviceName) 仍是 ProxyTracer 时,才会:

typescript
new NodeSDK({ ...options, serviceName, instrumentations }).start()

其它导出

导出说明
getTracer()包装后的 tracer(startActiveSpan 自动 end / 记错)
startActiveSpan / record在当前上下文创建活跃 span
getCurrentSpan()读当前 span(若有)
setAttributes(attrs)给当前 span 设属性;返回是否设置成功
contextKeySpanOTel 内部 span context key(Symbol
Tracer / StartSpan / StartActiveSpan / ActiveSpanArgs / VafastOpenTelemetryOptions类型

最佳实践

  1. 生产用 preload 初始化 SDK;中间件只负责 HTTP 根 span
  2. Span 名用稳定业务语义(db.get-userapi.create-order),避免把用户 ID 等高基数动态值塞进名字
  3. 高流量配置 sampler,控制导出量与成本
  4. 属性里不要写密码、token、完整请求体等敏感数据
  5. 子操作用 startActiveSpan;不要为每个循环迭代开 span
  6. 跨服务依赖传播时,优先统一 propagator,并配合 auto-instrumentations

注意事项

  • 成功路径会 clone 响应用于估算 body size,大响应有额外开销
  • User-Agent 不会写入 http.request.header.*
  • 未预加载且首次调用时会启动 NodeSDK;与其它库抢全局 OTel 时注意初始化顺序
  • getCurrentSpan 依赖内部 context key,跨库混用时可能拿不到 span
  • 本包不替代完整的 metrics / logs 方案

相关链接