Hono OpenTelemetry 插桩 - 发送跟踪和指标
本指南介绍如何使用 OpenTelemetry 对 Hono 应用进行插桩,并将 Trace 和 Metrics 数据发送到 SigNoz。@hono/otel 中间件会为每个请求创建一个 Span,命名规则基于路由,并记录 HTTP 请求指标。OpenTelemetry Node.js SDK 负责导出数据,并追踪路由触发的调用,例如 fetch 请求和数据库查询。主要流程基于 Node.js 运行 Hono。若使用 Bun,请参阅在 Bun 上插桩 Hono。
大部分步骤相同。请按Cloud 迁移至自托管中的说明,更新端点并移除 ingestion key 请求头,以适配本指南。
先决条件
- Node.js 22.15 或更高版本。当前 OpenTelemetry SDK 包需要此版本。
- SigNoz 实例(Cloud 或 自托管版本)
- 基于 Node.js 的 Hono 应用。创建命令:
npm create hono@latest my-app -- --template nodejs。
已在 Node.js v22.23.2、Hono 4.13.13、@hono/otel 1.2.0 和 @opentelemetry/sdk-node 0.300.0 环境下测试。
从 Hono 应用发送遥测数据
步骤 1. 安装依赖包
在项目根目录运行以下命令:
复制npm install @hono/otel \
@opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/instrumentation
步骤 2. 创建插桩文件
创建 src/instrumentation.ts。该文件会在应用加载前启动 OpenTelemetry SDK:
import { register } from 'node:module'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
// 让 OpenTelemetry 补丁 ESM 应用导入的模块
register('@opentelemetry/instrumentation/hook.mjs', import.meta.url)
const sdk = new NodeSDK({
instrumentations: [
getNodeAutoInstrumentations({
// @hono/otel 会创建服务器 span,因此跳过重复的 HTTP 服务器 span
'@opentelemetry/instrumentation-http': {
disableIncomingRequestInstrumentation: true,
},
}),
],
})
sdk.start()
process.on('SIGTERM', () => {
sdk.shutdown().finally(() => process.exit(0))
})
这个文件主要做三件事:
register()调用添加了 OpenTelemetry 加载器钩子。Hono Node.js 模板是一个 ECMAScript Module(ESM)应用,如果没有这个钩子,SDK 就无法追踪发往node:http的请求。disableIncomingRequestInstrumentation阻止 HTTP 插桩为每个请求创建第二个服务器 span。发出的 HTTP 调用仍会被追踪。SIGTERM处理器在应用停止前,将剩余数据发送到 SigNoz。
SDK 会从环境变量读取端点、摄取密钥和服务名称,这些变量是在第 4 步中设置的。
步骤 3:将中间件添加到应用中
在 src/index.ts 中,将 httpInstrumentationMiddleware() 添加在路由之前:
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { httpInstrumentationMiddleware } from '@hono/otel'
const app = new Hono()
app.use(httpInstrumentationMiddleware())
app.get('/', (c) => {
return c.text('Hello Hono!')
})
serve({
fetch: app.fetch,
port: 3000
}, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
然后更新 package.json 中的 dev 和 start 脚本,使 Node.js 先加载插桩文件:
"scripts": {
"dev": "tsx watch --import ./src/instrumentation.ts src/index.ts",
"build": "tsc",
"start": "node --import ./dist/instrumentation.js dist/index.js"
}
步骤 4:设置环境变量并运行
VMKubernetesDockerWindows在启动应用的 shell 中设置这些变量,然后构建并运行:
Copyexport OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_SERVICE_NAME="<service-name>"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=<environment>"
npm run build
npm start将这些变量添加到部署清单的容器 spec 中:
Copyenv:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "https://ingest.<region>.signoz.cloud:443"
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "signoz-ingestion-key=<your-ingestion-key>"
- name: OTEL_SERVICE_NAME
value: "<service-name>"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "deployment.environment.name=<environment>"使用 Docker 标签页中的 Dockerfile 构建容器镜像。要从本机发送测试请求,需将本地端口转发到该部署。把 <deployment-name> 替换为你的部署名称:
kubectl port-forward deploy/<deployment-name> 3000:3000然后在另一个终端执行 curl http://localhost:3000/。
在项目根目录创建 Dockerfile:
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["node", "--import", "./dist/instrumentation.js", "dist/index.js"]在旁边创建一个 .dockerignore 文件,避免构建时把本地的 node_modules 和 dist 文件夹复制进去:
node_modules
dist构建镜像,并在运行时传入这些变量:
Copydocker build -t hono-app .
docker run \
-e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
-e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
-e OTEL_SERVICE_NAME="<service-name>" \
-e OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=<environment>" \
-p 3000:3000 \
hono-app在 PowerShell 中设置这些变量,然后构建并运行应用:
Copy$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:OTEL_SERVICE_NAME = "<service-name>"
$env:OTEL_RESOURCE_ATTRIBUTES = "deployment.environment.name=<environment>"
npm run build
npm start
验证以下取值:
<region>:你的 SigNoz Cloud 区域。<your-ingestion-key>:你的 SigNoz 摄入密钥。<service-name>:SigNoz 中显示的服务名称,例如hono-app。<environment>:部署环境,例如production。
向你的应用发送几个请求,例如 curl http://localhost:3000/。在本地开发中,使用相同的变量运行 npm run dev 也会发送数据。
在 Bun 上为 Hono 添加监控
在 Bun 中,同样的中间件会创建请求 span 和 HTTP 指标。Bun 不需要 loader hook。Bun 内置的 fetch 不支持追踪,因此你的路由通过 fetch 发起的调用不会产生独立的 span。
安装以下包:
Copybun add @hono/otel @opentelemetry/api @opentelemetry/sdk-node
创建 src/instrumentation.ts:
import { NodeSDK } from '@opentelemetry/sdk-node'
const sdk = new NodeSDK()
sdk.start()
process.on('SIGTERM', () => {
sdk.shutdown().finally(() => process.exit(0))
})
在 src/index.ts 中添加中间件:
import { Hono } from 'hono'
import { httpInstrumentationMiddleware } from '@hono/otel'
const app = new Hono()
app.use(httpInstrumentationMiddleware())
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app
设置与第 4 步相同的环境变量,然后预加载监控文件来启动应用:
Copybun --preload ./src/instrumentation.ts src/index.ts
已在 Bun v1.4.3 上测试。
验证
运行已添加监控的应用后,请确保 traces 和 metrics 已到达 SigNoz:
- 向你的应用发送几个请求。
- 点击 Services 标签,找到在
OTEL_SERVICE_NAME中设置的服务名称。每个操作都带有路由名称,例如GET /users/:id。 - 等待约一分钟让首批指标完成导出,然后在 Metrics Explorer 中搜索
http.server.request.duration。


局限性
@hono/otel仅为整个请求创建一个 span。单独的 Hono 中间件不会生成独立的 span。@hono/otel仅发送两个 HTTP 指标:http.server.request.duration和http.server.active_requests。- 在 Cloudflare Workers 上,OpenTelemetry Node.js SDK 无法运行。若要追踪 Workers 上的 Hono 应用,请参阅 Cloudflare Workers OpenTelemetry。
故障排除
仍然没有数据?请参阅 调试缺失的 trace、日志和指标。
每个请求包含两个 server span
- 可能原因:HTTP instrumentation 创建了一个名为
GET且不带路由的 server span,而 Hono span 是其子 span。 - 解决方法:按照第 2 步所示,将
@opentelemetry/instrumentation-http的disableIncomingRequestInstrumentation设置为true。不要为此使用ignoreIncomingRequestHook,因为它会停止整个请求的追踪,导致 Hono span 不被发送。 - 验证:每个请求只显示一个以路由命名的 span,例如
GET /。
出站 HTTP 调用没有 span
- 可能原因:缺少
register('@opentelemetry/instrumentation/hook.mjs', import.meta.url)调用。在 ESM 应用中,如果没有这个 loader hook,SDK 无法对node:http打补丁。 - 解决办法:添加