otel-nextjs-style
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOTel Next.js Style
OTel Next.js 风格
For Next.js apps, prefer the framework entrypoint.
ts
// instrumentation.ts
import { registerOTel } from "@vercel/otel";
export function register() {
registerOTel({
serviceName: "mugline-web",
});
}Do not replace this with a custom bootstrap unless the repo is not a
normal Next/Vercel app or already has a custom provider that must be extended.
NodeSDKFor JavaScript/TypeScript LLM providers, prefer provider instrumentation over
manual child spans. For Anthropic, add OpenInference in the same bootstrap and
keep call sites native. This example uses ; if the installed
types are v1, use singular instead.
@vercel/otel@2.xlogRecordProcessorts
import Anthropic from "@anthropic-ai/sdk";
import { AnthropicInstrumentation } from "@arizeai/openinference-instrumentation-anthropic";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { registerOTel } from "@vercel/otel";
const anthropicInstrumentation = new AnthropicInstrumentation({
traceConfig: {
hideInputs: true,
hideOutputs: true,
},
});
anthropicInstrumentation.manuallyInstrument(Anthropic);
export function register() {
registerOTel({
serviceName: "mugline-web",
instrumentations: [anthropicInstrumentation],
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())],
});
}对于Next.js应用,优先使用框架入口文件。
ts
// instrumentation.ts
import { registerOTel } from "@vercel/otel";
export function register() {
registerOTel({
serviceName: "mugline-web",
});
}除非项目不是常规的Next/Vercel应用,或者已经有必须扩展的自定义提供者,否则不要用自定义的启动引导替换此配置。
NodeSDK对于JavaScript/TypeScript LLM提供者,优先使用提供者插装而非手动子跨度。对于Anthropic,在同一启动引导中添加OpenInference,并保持调用站点原生。此示例使用;如果安装的类型是v1版本,请改用单数形式的。
@vercel/otel@2.xlogRecordProcessorts
import Anthropic from "@anthropic-ai/sdk";
import { AnthropicInstrumentation } from "@arizeai/openinference-instrumentation-anthropic";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { registerOTel } from "@vercel/otel";
const anthropicInstrumentation = new AnthropicInstrumentation({
traceConfig: {
hideInputs: true,
hideOutputs: true,
},
});
anthropicInstrumentation.manuallyInstrument(Anthropic);
export function register() {
registerOTel({
serviceName: "mugline-web",
instrumentations: [anthropicInstrumentation],
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())],
});
}Route Handlers
路由处理器
Use native OTel APIs where auto-instrumentation is blind.
ts
import { withSpan } from "@superlog/otel-helpers";
const tracer = trace.getTracer("mugline.web");
const meter = metrics.getMeter("mugline.web");
const requests = meter.createCounter("mug.copy.generated");
export async function POST(request: Request) {
const tenantId = request.headers.get("x-tenant-id") ?? "tenant_demo";
return await withSpan("mug.copy.generate", async (span) => {
span.setAttribute("tenant.id", tenantId);
requests.add(1, { "tenant.id": tenantId, outcome: "success" });
return Response.json({ ok: true });
}, { tracer });
}For TypeScript route handlers, use for
bounded business spans and add to when it
is not already present. This is required when the package can be installed. It
keeps span lifecycle/error handling out of the handler body and avoids a large
indentation diff. Do not expand the whole route into
plus / / unless the helper
cannot be added or the span has a true cross-callback lifecycle.
@superlog/otel-helperswithSpan@superlog/otel-helperspackage.jsontracer.startActiveSpan(...)trycatchfinallyIf a route has an LLM call and OpenInference/provider instrumentation supports
that SDK, do not wrap the provider call. Leave /
equivalent in place and put business context on the active product span or
structured log. Do not duplicate provider/model/token attributes in route-level
spans, logs, or metrics when OpenInference already reports them. Do not calculate
LLM cost in route handlers; Superlog derives estimated cost in the UI/query layer
from OpenInference provider/model/token attributes.
For Anthropic in Next.js/ESM, keep the instrumentation instance and
call at module scope so it runs once and before
route code.
client.messages.create(...)manuallyInstrument(Anthropic)Match the logs option to the installed package/types:
uses singular, while
uses plural. For normal
Next.js/Vercel apps, do not guard
behind ; Next calls in
the appropriate runtime and handles its own runtime differences.
@vercel/otel@vercel/otel@1.xlogRecordProcessor@vercel/otel@2.xlogRecordProcessorsregisterOTel(...)NEXT_RUNTIMEinstrumentation.ts@vercel/otelconsole.info@opentelemetry/api-logsconsole.*ts
logger.emit({
severityNumber: SeverityNumber.INFO,
severityText: "INFO",
body: "generated mug copy",
attributes: {
"tenant.id": tenantId,
"gen_ai.provider.name": "anthropic",
"gen_ai.request.model": model,
"app.gen_ai.use_case": "web.mug_copy",
outcome: "success",
},
});在自动插装无法覆盖的地方,使用原生OTel API。
ts
import { withSpan } from "@superlog/otel-helpers";
const tracer = trace.getTracer("mugline.web");
const meter = metrics.getMeter("mugline.web");
const requests = meter.createCounter("mug.copy.generated");
export async function POST(request: Request) {
const tenantId = request.headers.get("x-tenant-id") ?? "tenant_demo";
return await withSpan("mug.copy.generate", async (span) => {
span.setAttribute("tenant.id", tenantId);
requests.add(1, { "tenant.id": tenantId, outcome: "success" });
return Response.json({ ok: true });
}, { tracer });
}对于TypeScript路由处理器,使用的来创建有界业务跨度,若项目中尚未安装,请将其添加到中。当该包可安装时,这是必需的操作。它可以将跨度生命周期/错误处理从处理器主体中剥离,避免大量缩进差异。除非无法添加该辅助工具,或者跨度具有真正的跨回调生命周期,否则不要将整个路由展开为加上//的形式。
@superlog/otel-helperswithSpan@superlog/otel-helperspackage.jsontracer.startActiveSpan(...)trycatchfinally如果路由包含LLM调用,且OpenInference/提供者插装支持该SDK,则不要包装提供者调用。保留或类似调用不变,并将业务上下文添加到活动产品跨度或结构化日志中。当OpenInference已报告提供者/模型/令牌属性时,不要在路由级跨度、日志或指标中重复这些属性。不要在路由处理器中计算LLM成本;Superlog会在UI/查询层从OpenInference的提供者/模型/令牌属性推导估算成本。
对于Next.js/ESM中的Anthropic,请将插装实例和调用放在模块作用域中,使其仅运行一次且在路由代码之前执行。
client.messages.create(...)manuallyInstrument(Anthropic)使的日志选项与已安装的包/类型匹配:使用单数形式的,而使用复数形式的。对于常规的Next.js/Vercel应用,不要在条件下包裹;Next会在适当的运行时调用,且会自行处理运行时差异。
@vercel/otel@vercel/otel@1.xlogRecordProcessor@vercel/otel@2.xlogRecordProcessorsNEXT_RUNTIMEregisterOTel(...)instrumentation.ts@vercel/otelconsole.info@opentelemetry/api-logsconsole.*ts
logger.emit({
severityNumber: SeverityNumber.INFO,
severityText: "INFO",
body: "generated mug copy",
attributes: {
"tenant.id": tenantId,
"gen_ai.provider.name": "anthropic",
"gen_ai.request.model": model,
"app.gen_ai.use_case": "web.mug_copy",
outcome: "success",
},
});Configuration And Smoke
配置与冒烟测试
Inline the endpoint and ingest key in — pass them
explicitly to . Don't rely on env vars:
the Superlog ingest key is project-scoped + write-only and inline config
sidesteps Vercel's env-propagation quirks during preview builds.
instrumentation.tsregisterOTelOTEL_EXPORTER_OTLP_*ts
const SUPERLOG_ENDPOINT = "https://intake.superlog.sh";
const SUPERLOG_KEY = "superlog_live_…"; // set by superlog-onboard skill on pairing
registerOTel({
serviceName: "mugline-web",
traceExporter: new OTLPTraceExporter({
url: `${SUPERLOG_ENDPOINT}/v1/traces`,
headers: { authorization: `Bearer ${SUPERLOG_KEY}` },
}),
// …same shape for log + metric exporters
});Smoke checks should use tools already in the repo, e.g. or
, plus a real app request where practical. Do not invent fragile
inline Node scripts that import TypeScript source files directly, and do not
assume exists unless it is already installed.
npm run typechecknpm run buildts-node在中内联端点和摄取密钥——将它们显式传递给。不要依赖环境变量:Superlog摄取密钥是项目范围的且仅可写,内联配置可以避免Vercel在预览构建期间的环境变量传播问题。
instrumentation.tsregisterOTelOTEL_EXPORTER_OTLP_*ts
const SUPERLOG_ENDPOINT = "https://intake.superlog.sh";
const SUPERLOG_KEY = "superlog_live_…"; // set by superlog-onboard skill on pairing
registerOTel({
serviceName: "mugline-web",
traceExporter: new OTLPTraceExporter({
url: `${SUPERLOG_ENDPOINT}/v1/traces`,
headers: { authorization: `Bearer ${SUPERLOG_KEY}` },
}),
// …same shape for log + metric exporters
});冒烟测试应使用项目中已有的工具,例如或,并尽可能结合真实的应用请求。不要创建脆弱的内联Node脚本直接导入TypeScript源文件,除非已安装,否则不要假设它存在。
npm run typechecknpm run buildts-node