mcpField
O
otel-node

otel-node

github.com/mizchi云与 DevOps41644 次安装1 周前更新
代码仓库

关于 otel-node

面向 Node.js 应用的 OpenTelemetry 技能,涵盖遥测初始化、追踪采集及导出故障排查,尤其关注 ESM 打包问题。适合需要接入追踪或诊断采集链路的开发者。

主要功能

  1. 01NodeSDK 初始化与 OTLP 导出
  2. 02HTTP、Express、PostgreSQL 和 Redis 自动埋点
  3. 03手动创建 Span 与上下文传播
  4. 04诊断并绕过 ESM 打包埋点故障
  5. 05验证 Collector 采集链路

适用场景

  1. 01为 Node.js 服务添加追踪
  2. 02排查 esbuild ESM 输出中没有 Span 的问题
  3. 03为 Hono 编写显式中间件埋点

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mizchi/skills --skill otel-node

安装后在 Claude Code 中输入/otel-node确认已加载。

附带文件README.md

详细介绍

OpenTelemetry — Node.js

SDK 初始化

// otel.ts — import before everything else
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { Resource } from "@opentelemetry/resources";
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";

const exporter = new OTLPTraceExporter({
  url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + "/v1/traces",
});

export const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: process.env.SERVICE_NAME ?? "my-service",
  }),
  spanProcessor: new BatchSpanProcessor(exporter),
});

sdk.start();
process.on("SIGTERM", () => sdk.shutdown());

在应用入口之前加载:node --import ./otel.js server.js 或 tsx --import ./otel.ts server.ts。

自动插桩包

只添加所需的包——每个包都会修补特定模块:

pnpm add @opentelemetry/instrumentation-http       # node:http / node:https
pnpm add @opentelemetry/instrumentation-express    # Express routing spans
pnpm add @opentelemetry/instrumentation-pg         # postgres queries
pnpm add @opentelemetry/instrumentation-ioredis    # Redis commands

在 SDK 初始化时注册:

import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";

export const sdk = new NodeSDK({
  instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()],
  // ...
});

Hono:没有官方插桩包。请使用手动中间件(见下文)。

esbuild ESM — 自动插桩静默失效

症状

应用正常启动,SDK 初始化日志也会出现,但没有 span 到达收集器。没有错误。以下情况会出现此问题:

  • 输出格式为 --format=esm
  • 使用 @opentelemetry/instrumentation-* 自动插桩
  • 打包工具为 esbuild(Vite / SWC 也一样,根本原因相同)
根本原因

@opentelemetry/instrumentation-* 使用 require-in-the-middle 钩住 require() 并对目标模块(node:http 等)进行猴子补丁。在 esbuild ESM bundle 中,import 会在打包时静态解析——运行时不会调用 require() → 钩子不会触发 → 不会应用补丁 → 没有 span。不会抛出错误(静默失效)。

修复方案 A — 手动创建 span(推荐用于 Hono/esbuild)

编写一个显式创建 span 的中间件:

import { context, propagation, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";

const tracer = trace.getTracer("app");

app.use(async (c, next) => {
  const ctx = propagation.extract(context.active(), c.req.raw.headers);
  const route = new URL(c.req.url).pathname;

  await tracer.startActiveSpan(
    `${c.req.method} ${route}`,
    { kind: SpanKind.SERVER, attributes: { "http.method": c.req.method, "http.route": route } },
    ctx,
    async (span) => {
      try {
        await next();
        span.setAttribute("http.status_code", c.res.status);
        span.setStatus({ code: SpanStatusCode.OK });
      } catch (e: any) {
        span.recordException(e);
        span.setStatus({ code: SpanStatusCode.ERROR, message: e?.message });
        throw e;
      } finally {
        span.end();
      }
    }
  );
});

通过元数据将上下文传播到发出的 gRPC 调用:

const callUnary = (method, req) => {
  const metadata = new grpc.Metadata();
  propagation.inject(context.active(), metadata, {
    set: (carrier, key, value) => (carrier as grpc.Metadata).set(key, value),
  });
  return new Promise((resolve, reject) =>
    client[method](req, metadata, (err, res) => (err ? reject(err) : resolve(res)))
  );
};
修复方案 B — 切换到 CJS 输出

将 esbuild 改为 --format=cjs。require() 钩子会正常触发。如果没有仅支持 ESM 的依赖,就可以使用。Hono 支持 CJS。

修复方案 C — 不进行打包

使用 tsx 或 node --loader ts-node/esm + --import @opentelemetry/auto-instrumentations-node/register。跳过对 Node 进程的打包。会增大容器镜像体积;不建议用于生产环境。

验证管道

临时向 OTel Collector 添加一个调试导出器:

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp/tempo, debug]

收到 span 时,收集器日志会显示 Trace ID: ... Name: GET /api/users。没有输出 = 收集器之前的环节存在问题。

相关内容

  • opentelemetry — 信号设计、span 命名、采样、W3C 传播
  • cloudflare-workers-otel-utels — Cloudflare Workers 遥测(无 Node 运行时,采用 fetch 边界方案)
  • esbuild、Vite 和 SWC 都存在相同的 require-in-the-middle 失效模式
  • 截至 2026-05,instrumentation-* 包不支持静态 import 修补

评论

支持 Markdown