如何为SvelteKit函数配置OpenTelemetry手动埋点并作为Trace首个Span
SvelteKit 中 OpenTelemetry 手动埋点解决方案
核心要点:区分客户端/服务端的 Tracer 获取方式
SvelteKit 的同构特性要求必须分开处理服务端和客户端的 OTel 初始化与 Tracer 获取逻辑,这是你之前导入出错和 Span 不显示的核心原因。
1. 服务端(+page.server.js/ts 或服务端加载函数)手动埋点
服务端复用已有的 NodeSDK,直接从 SDK 获取 Tracer,确保自定义 Span 作为 Trace 的第一个条目(根节点)。
示例代码:
// 假设你的 NodeSDK 初始化文件在 src/lib/otel/server.ts import { tracer } from '$lib/otel/server'; export async function load() { // 创建根 Span const rootSpan = tracer.startSpan('getContactsAndUpdateStore', { kind: 1, // SpanKind.INTERNAL attributes: { 'component': 'sveltekit-page-load', 'page': '/contacts' } }); try { // 包裹业务逻辑 const contacts = await fetchContacts(); // 执行更新 Store 的逻辑 rootSpan.setStatus({ code: 0 }); // StatusCode.OK return { contacts }; } catch (error) { rootSpan.setStatus({ code: 2, // StatusCode.ERROR message: error instanceof Error ? error.message : 'Unknown error' }); rootSpan.recordException(error); throw error; } finally { // 必须结束 Span,否则无法上报 rootSpan.end(); } }
2. 客户端(+page.svelte 或组件)手动埋点
客户端需要单独初始化 Web SDK,不能直接使用服务端的 NodeSDK,同时要避免服务端渲染时导入客户端依赖。
第一步:客户端 OTel 初始化(src/lib/otel/client.ts)
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'; import { ZoneContextManager } from '@opentelemetry/context-zone'; let tracer; // 仅在浏览器环境下初始化 if (typeof window !== 'undefined') { const provider = new WebTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({ url: 'http://你的Tempo采集地址:4318/v1/traces' }))); provider.register({ contextManager: new ZoneContextManager() }); registerInstrumentations({ instrumentations: [new FetchInstrumentation()] }); tracer = provider.getTracer('sveltekit-client'); } export { tracer };
第二步:在 +page.svelte 中使用客户端 Tracer
<script> import { onMount } from 'svelte'; let tracer; // 确保在客户端挂载后加载 Tracer onMount(async () => { const { tracer: clientTracer } = await import('$lib/otel/client'); tracer = clientTracer; }); async function getContactsAndUpdateStore() { if (!tracer) return; // 创建客户端根 Span const span = tracer.startSpan('getContactsAndUpdateStore', { kind: 1, attributes: { 'component': 'sveltekit-page-component' } }); try { // 业务逻辑 const res = await fetch('/api/contacts'); const contacts = await res.json(); // 更新 Store 操作 span.setStatus({ code: 0 }); } catch (error) { span.setStatus({ code: 2, message: error.message }); span.recordException(error); } finally { span.end(); } } </script> <button on:click={getContactsAndUpdateStore}>加载联系人</button>
3. 自定义 Span 不显示的关键排查点
- 必须调用
span.end():未结束的 Span 不会被上报到 Tempo。 - 上下文关联:如果需要让自动埋点的 Span 成为自定义 Span 的子节点,需在业务逻辑中传递上下文:
import { context, trace } from '@opentelemetry/api'; // 在根 Span 上下文下执行业务逻辑 const contacts = await context.with(trace.setSpan(context.active(), rootSpan), () => fetchContacts()); - 客户端初始化时机:通过
typeof window !== 'undefined'确保仅在浏览器环境初始化,避免服务端渲染报错。 - Exporter 配置检查:确认 Tempo 采集地址、端口正确,且服务端配置了允许客户端上报的 CORS 规则。
内容的提问来源于stack exchange,提问作者Voran
相关产品推荐
相关产品推荐

