title: OpenTelemetry 插件 - ElysiaJS head: - - meta - property: 'og:title' content: OpenTelemetry 插件 - ElysiaJS
- - meta
- name: 'description'
content: 一个为 Elysia 添加 OpenTelemetry 支持的插件。通过运行 "bun add @elysiajs/opentelemetry" 开始安装该插件。
- - meta
- name: 'og:description'
content: 一个为 Elysia 添加 OpenTelemetry 支持的插件。通过运行 "bun add @elysiajs/opentelemetry" 开始安装该插件。
OpenTelemetry
要开始使用 OpenTelemetry,请安装 @elysiajs/opentelemetry 并将插件应用于任何实例。
import { Elysia } from 'elysia'
import { opentelemetry } from '@elysiajs/opentelemetry'
new Elysia()
.use(opentelemetry())
为什么在 Elysia 中使用 OpenTelemetry?
- 1 行配置
- Span 名称即为函数名
- 将相关的生命周期分组在一起
- 包装代码以记录特定部分
- 支持 Server-Sent Event 和响应流
- 兼容任何兼容 OpenTelemetry 的库
您可以将遥测数据导出到 Jaeger、Zipkin、New Relic、Axiom 或任何其他兼容 OpenTelemetry 的后端。
导出 OpenTelemetry 数据
我们可以将 OpenTelemetry 数据导出到任何支持 OpenTelemetry 协议的后端。
以下是将遥测数据导出到 Axiom 的示例:
import { Elysia } from 'elysia'
import { opentelemetry } from '@elysiajs/opentelemetry'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
new Elysia().use(
opentelemetry({
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: `Bearer ${Bun.env.AXIOM_TOKEN}`,
'X-Axiom-Dataset': Bun.env.AXIOM_DATASET
}
})
)
]
})
)OpenTelemetry SDK
Elysia OpenTelemetry 仅用于将 OpenTelemetry 应用于 Elysia 服务器。
您可以正常使用 OpenTelemetry SDK,如果 span 在 Elysia 的请求 span 下运行,它将自动出现在 Elysia 的追踪中。
但是,我们也提供了 getTracer 和 record 工具函数,用于从应用程序的任何部分收集 span。
import { Elysia } from 'elysia'
import { record } from '@elysiajs/opentelemetry'
export const plugin = new Elysia().get('', () => {
return record('数据库查询', () => {
return db.query('SELECT * FROM users')
})
})record 工具函数
record 等同于 OpenTelemetry 的 startActiveSpan,但它会自动处理关闭和捕获异常。
您可以将 record 视为代码的标签,它将在追踪中显示。
准备您的代码库以实现可观测性
Elysia OpenTelemetry 将对生命周期进行分组,并将每个钩子的函数名读取为 span 的名称。
现在是为您的函数命名的好时机。
如果您的钩子处理器是箭头函数,您可以将其重构为命名函数以便更好地理解追踪,否则您的追踪 span 将被命名为 anonymous。
const bad = new Elysia()
// ⚠️ span 名称将是 anonymous
.derive(async ({ cookie: { session } }) => {
return {
user: await getProfile(session)
}
})
const good = new Elysia()
// ✅ span 名称将是 getProfile
.derive(async function getProfile({ cookie: { session } }) {
return {
user: await getProfile(session)
}
})getCurrentSpan
当您在处理器之外时,getCurrentSpan 是一个用于获取当前请求的当前 span 的工具函数。
import { getCurrentSpan } from '@elysiajs/opentelemetry'
function utility() {
const span = getCurrentSpan()
span.setAttributes({
'custom.attribute': 'value'
})
}它通过从 AsyncLocalStorage 检索当前 span 来在处理器之外工作。
setAttributes
setAttributes 是一个用于为当前 span 设置属性的工具函数。
import { setAttributes } from '@elysiajs/opentelemetry'
function utility() {
setAttributes({
'custom.attribute': 'value'
})
}这是 getCurrentSpan().setAttributes 的语法糖。
配置
有关配置选项和定义,请参阅 opentelemetry 插件。
Instrumentations 高级概念
许多 instrumentation 库要求 SDK 必须在导入模块之前运行。
例如,要使用 PgInstrumentation,OpenTelemetry SDK 必须在导入 pg 模块之前运行。
要在 Bun 中实现这一点,我们可以:
- 将 OpenTelemetry 设置分离到另一个文件中
- 创建
bunfig.toml来预加载 OpenTelemetry 设置文件
让我们在 src/instrumentation.ts 中创建一个新文件:
import { opentelemetry } from '@elysiajs/opentelemetry'
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'
export const instrumentation = opentelemetry({
instrumentations: [new PgInstrumentation()]
})然后我们可以将这个 instrumentaiton 插件应用到 src/index.ts 中的主实例:
import { Elysia } from 'elysia'
import { instrumentation } from './instrumentation.ts'
new Elysia().use(instrumentation).listen(3000)接着创建一个包含以下内容的 bunfig.toml:
preload = ["./src/instrumentation.ts"]这将告诉 Bun 在运行 src/index.ts 之前加载并设置 instrumentation,从而允许 OpenTelemetry 根据需要进行设置。
部署到生产环境 高级概念
如果您正在使用 bun build 或其他打包器。
由于 OpenTelemetry 依赖于对 node_modules/<library> 的 monkey-patching。为了确保 instrumentations 正常工作,我们需要指定要被 instrument 的库是一个外部模块,以将其排除在打包之外。
例如,如果您正在使用 @opentelemetry/instrumentation-pg 来 instrument pg 库。我们需要排除 pg 不被打包,并确保它从 node_modules/pg 导入。
为了实现这一点,我们可以使用 --external pg 将 pg 指定为外部模块:
bun build --compile --external pg --outfile server src/index.ts这告诉 bun 不要将 pg 打包到最终的输出文件中,并将在运行时从 node_modules 目录导入。因此,在生产服务器上,您也必须保留 node_modules 目录。
建议将在生产服务器中需要的包在 package.json 中指定为 dependencies,并使用 bun install --production 来仅安装生产依赖。
{
"dependencies": {
"pg": "^8.15.6"
},
"devDependencies": {
"@elysiajs/opentelemetry": "^1.2.0",
"@opentelemetry/instrumentation-pg": "^0.52.0",
"@types/pg": "^8.11.14",
"elysia": "^1.2.25"
}
}然后,在运行构建命令后,在生产服务器上:
bun install --production如果 node_modules 目录仍然包含开发依赖,您可以删除 node_modules 目录并重新安装生产依赖。