Skip to content

OpenTelemetry

要开始使用 OpenTelemetry,请安装 @elysiajs/opentelemetry 并将插件应用于任何实例。

¥To start using OpenTelemetry, install @elysiajs/opentelemetry and apply plugin to any instance.

typescript
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())]
	})
)

jaeger showing collected trace automatically

Elysia OpenTelemetry 将收集任何兼容 OpenTelemetry 标准的库的 span,并自动应用父级和子级 span。

¥Elysia OpenTelemetry will collect span of any library compatible with OpenTelemetry standard, and will apply parent and child span automatically.

在上面的代码中,我们应用 Prisma 来跟踪每个查询的耗时。

¥In the code above, we apply Prisma to trace how long each query took.

通过应用 OpenTelemetry,Elysia 将:

¥By applying OpenTelemetry, Elysia will then:

  • 收集遥测数据

  • 将相关生命周期分组

  • 测量每个函数的执行时间

  • 检测 HTTP 请求和响应

  • 收集错误和异常

你可以将遥测数据导出到 Jaeger、Zipkin、New Relic、Axiom 或任何其他兼容 OpenTelemetry 的后端。

¥You may export telemetry data to Jaeger, Zipkin, New Relic, Axiom or any other OpenTelemetry compatible backend.

axiom showing collected trace from OpenTelemetry

以下是将遥测数据导出到 Axiom 的示例

¥Here's an example of exporting telemetry to Axiom

typescript
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
					} 
				})
			)
		]
	})
)

仪器

¥Instrumentations

许多插桩库要求 SDK 必须在导入模块之前运行。

¥Many instrumentation libraries required that the SDK MUST run before importing the module.

例如,要使用 PgInstrumentation,必须在导入 pg 模块之前运行 OpenTelemetry SDK

¥For example, to use PgInstrumentation, the OpenTelemetry SDK must run before importing the pg module.

要在 Bun 中实现这一点,我们可以

¥To achieve this in Bun, we can

  1. 将 OpenTelemetry 设置分离到另一个文件中
  2. 创建 bunfig.toml 以预加载 OpenTelemetry 设置文件

让我们在 src/instrumentation.ts 中创建一个新文件

¥Let's create a new file in src/instrumentation.ts

ts
import { opentelemetry } from '@elysiajs/opentelemetry'
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'

export const instrumentation = opentelemetry({
	instrumentations: [new PgInstrumentation()]
})

然后我们可以将这个 instrumentaiton 插件应用到 src/index.ts 的主实例中。

¥Then we can apply this instrumentaiton plugin into our main instance in src/index.ts

ts
import { Elysia } from 'elysia'
import { instrumentation } from './instrumentation.ts'

new Elysia().use(instrumentation).listen(3000)

然后创建一个 bunfig.toml,内容如下:

¥Then create a bunfig.toml with the following:

toml
preload = ["./src/instrumentation.ts"]

这将告诉 Bun 在运行 src/index.ts 之前加载并设置 instrumentation,以便 OpenTelemetry 根据需要进行设置。

¥This will tell Bun to load and setup instrumentation before running the src/index.ts allowing OpenTelemetry to do its setup as needed.

部署到生产环境

¥Deploying to production

如果你使用 bun build 或其他打包器。

¥If you are using bun build or other bundlers.

由于 OpenTelemetry 依赖于 monkey-patching node_modules/<library>。为了使 make instrumentations 正常工作,我们需要将要被 instrument 的库指定为外部模块,以将其排除在打包之外。

¥As OpenTelemetry rely on monkey-patching node_modules/<library>. It's required that make instrumentations works properly, we need to specify that libraries to be instrument is an external module to exclude it from being bundled.

例如,如果你使用 @opentelemetry/instrumentation-pg 来检测 pg 库。我们需要将 pg 从打包包中排除,并确保它导入 node_modules/pg

¥For example, if you are using @opentelemetry/instrumentation-pg to instrument pg library. We need to exclude pg from being bundled and make sure that it is importing node_modules/pg.

为了使其正常工作,我们可以使用 --external pgpg 指定为外部模块。

¥To make this works, we may specified pg as an external module with --external pg

bash
bun build --compile --external pg --outfile server src/index.ts

这告诉 bun 不要将 pg 打包到最终输出文件中,而是在运行时从 node_modules 目录导入。所以,在生产服务器上,你还必须保留 node_modules 目录。

¥This tells bun to not pg bundled into the final output file, and will be imported from the node_modules directory at runtime. So on a production server, you must also keeps the node_modules directory.

建议在 package.json 中将生产服务器中应可用的软件包指定为依赖,并使用 bun install --production 仅安装生产依赖。

¥It's recommended to specify packages that should be available in a production server as dependencies in package.json and use bun install --production to install only production dependencies.

json
{
	"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"
	}
}

然后在生产服务器上运行构建命令后,

¥Then after running a build command, on a production server

bash
bun install --production

如果 node_modules 目录仍然包含开发依赖,你可以删除 node_modules 目录并重新安装生产依赖。

¥If the node_modules directory still includes development dependencies, you may remove the node_modules directory and reinstall production dependencies again.

OpenTelemetry SDK

Elysia OpenTelemetry 仅用于将 OpenTelemetry 应用于 Elysia 服务器。

¥Elysia OpenTelemetry is for applying OpenTelemetry to Elysia server only.

你可以正常使用 OpenTelemetry SDK,并且 span 在 Elysia 的请求 span 下运行,它将自动显示在 Elysia 跟踪中。

¥You may use OpenTelemetry SDK normally, and the span is run under Elysia's request span, it will be automatically appear in Elysia trace.

然而,我们还提供了 getTracerrecord 实用程序,用于从应用的任何部分收集 span。

¥However, we also provide a getTracer, and record utility to collect span from any part of your application.

typescript
import { Elysia } from 'elysia'
import { record } from '@elysiajs/opentelemetry'

export const plugin = new Elysia().get('', () => {
	return record('database.query', () => {
		return db.query('SELECT * FROM users')
	})
})

记录实用程序

¥Record utility

record 相当于 OpenTelemetry 的 startActiveSpan,但它可以处理自动关闭并自动捕获异常。

¥record is an equivalent to OpenTelemetry's startActiveSpan but it will handle auto-closing and capture exception automatically.

你可以将 record 视为代码的标签,该标签将显示在跟踪中。

¥You may think of record as a label for your code that will be shown in trace.

为可观察性准备代码库

¥Prepare your codebase for observability

Elysia OpenTelemetry 将对生命周期进行分组,并将每个钩子的函数名称读取为 span 的名称。

¥Elysia OpenTelemetry will group lifecycle and read the function name of each hook as the name of the span.

现在是时候命名你的函数了。

¥It's a good time to name your function.

如果你的钩子处理程序是箭头函数,你可以将其重构为命名函数以便更好地理解跟踪,否则,你的跟踪跨度将被命名为 anonymous

¥If your hook handler is an arrow function, you may refactor it to named function to understand the trace better, otherwise your trace span will be named as anonymous.

typescript
const bad = new Elysia()
	// ⚠️ span name will be anonymous
	.derive(async ({ cookie: { session } }) => {
		return {
			user: await getProfile(session)
		}
	})

const good = new Elysia()
	// ✅ span name will be getProfile
	.derive(async function getProfile({ cookie: { session } }) {
		return {
			user: await getProfile(session)
		}
	})

getCurrentSpan

getCurrentSpan 是一个实用程序,用于在处理程序之外获取当前请求的当前跨度。

¥getCurrentSpan is a utility to get the current span of the current request when you are outside of the handler.

typescript
import { getCurrentSpan } from '@elysiajs/opentelemetry'

function utility() {
	const span = getCurrentSpan()
	span.setAttributes({
		'custom.attribute': 'value'
	})
}

这在处理程序之外工作,通过从 AsyncLocalStorage 检索当前跨度来实现。

¥This works outside of the handler by retriving current span from AsyncLocalStorage

setAttributes

setAttributes 是一个实用程序,用于将属性设置为当前跨度。

¥setAttributes is a utility to set attributes to the current span.

typescript
import { setAttributes } from '@elysiajs/opentelemetry'

function utility() {
	setAttributes({
		'custom.attribute': 'value'
	})
}

这是 getCurrentSpan().setAttributes 的语法糖

¥This is a syntax sugar for getCurrentSpan().setAttributes

配置

¥Configuration

有关配置选项和定义,请参阅 opentelemetry 插件

¥See opentelemetry plugin for configuration option and definition.