← 中文资源

简体中文版本

OpenTelemetry Node 追踪跨度

用 OpenTelemetry 为 Node 服务建立追踪:父子 span 嵌套跨越异步边界、属性与异常记录进 span、OTLP 批量导出到采集器。

阅读英文原文

示例说明

日志回答发生了什么,追踪回答路径上每一步花了多久。startActiveSpan 把当前 span 设为上下文,其内创建的 span 自动成为子节点,异步 await 不会丢失父子关系——一次订单处理的验证与扣款作为子 span 挂在处理根下,瀑布图一目了然。recordException 把异常与堆栈记进 span,状态码标红失败节点。

技术要点

  • startActiveSpan 自动传播父子上下文
  • setAttribute 为 span 附加业务维度
  • shutdown 冲刷缓冲区保证数据落地

代码解读

SDK 配置服务名、OTLP 导出端点与自动埋点;processOrder 的根 span 记录订单号属性,两个子 span 分别模拟校验与扣款耗时,扣款 span 记录异常并置错误状态。根 span 的 finally 确保 OK 与 ERROR 路径都正确收尾,shutdown 让批量导出器把缓冲数据发出。

使用建议

span 属性避免放高基数用户数据,用订单号而非用户全量字段。采样率按流量权衡成本。采集器是数据出口的单点,其可用性影响业务延迟感知。

代码示例

import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { trace, context, SpanStatusCode } from '@opentelemetry/api';

const sdk = new NodeSDK({
  serviceName: 'orders-service',
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTLP_ENDPOINT ?? 'http://collector:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

await sdk.start();
const tracer = trace.getTracer('orders', '1.0.0');

async function processOrder(orderId: string): Promise<void> {
  return tracer.startActiveSpan('order.process', async (span) => {
    span.setAttribute('order.id', orderId);
    try {
      await tracer.startActiveSpan('order.validate', async (child) => {
        await new Promise((r) => setTimeout(r, 10));
        child.setAttribute('validation.rules', 12);
        child.end();
      });

      await tracer.startActiveSpan('order.charge', async (child) => {
        await new Promise((r) => setTimeout(r, 25));
        child.recordException(new Error('card declined simulated'));
        child.setStatus({ code: SpanStatusCode.ERROR });
        child.end();
      });

      span.setStatus({ code: SpanStatusCode.OK });
    } catch (err) {
      span.recordException(err as Error);
      span.setStatus({ code: SpanStatusCode.ERROR });
    } finally {
      span.end();
    }
  });
}

await processOrder('ord-7');
await sdk.shutdown();