otel-nextjs-style

Next.js/Vercel OpenTelemetry style: instrumentation.ts, @vercel/otel bootstrap, native @opentelemetry/api call sites, inline public ingest token, and no raw NodeSDK replacement.

Skill file

Preview skill file
---
name: otel-nextjs-style
description: "Next.js/Vercel OpenTelemetry style: instrumentation.ts, @vercel/otel bootstrap, native @opentelemetry/api call sites, inline public ingest token, and no raw NodeSDK replacement."
---

# OTel Next.js Style

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 `NodeSDK` bootstrap unless the repo is not a
normal Next/Vercel app or already has a custom provider that must be extended.

For 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 `@vercel/otel@2.x`; if the installed
types are v1, use `logRecordProcessor` singular instead.

```ts
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 `@superlog/otel-helpers` `withSpan` for
bounded business spans and add `@superlog/otel-helpers` to `package.json` 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
`tracer.startActiveSpan(...)` plus `try` / `catch` / `finally` unless the helper
cannot be added or the span has a true cross-callback lifecycle.

If a route has an LLM call and OpenInference/provider instrumentation supports
that SDK, do not wrap the provider call. Leave `client.messages.create(...)` /
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
`manuallyInstrument(Anthropic)` call at module scope so it runs once and before
route code.

Match the `@vercel/otel` logs option to the installed package/types:
`@vercel/otel@1.x` uses `logRecordProcessor` singular, while
`@vercel/otel@2.x` uses `logRecordProcessors` plural. For normal
Next.js/Vercel apps, do not guard
`registerOTel(...)` behind `NEXT_RUNTIME`; Next calls `instrumentation.ts` in
the appropriate runtime and `@vercel/otel` handles its own runtime differences.

`console.info` is not OTLP log export. If there is no existing logger bridge,
use `@opentelemetry/api-logs` for production log records. Remove pre-existing
`console.*` calls that duplicate the same structured OTel log event:

```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

Use the source-level public Superlog configuration pattern from
`otel-onboarding-style` inside `instrumentation.ts`, then pass those constants
explicitly to `registerOTel`. The public project token is write-only and belongs
with the endpoint in the setup block, like a PostHog project token or Sentry
DSN.

```ts
const SUPERLOG_ENDPOINT = "https://intake.superlog.sh";
const SUPERLOG_PUBLIC_TOKEN = "sl_public_...";

// The token MUST be sent as the `x-api-key` header. Ingest only reads
// `x-api-key` or `Authorization: Bearer <token>`; any other header name 401s.
function superlogHeaders(token: string): Record<string, string> {
  return { "x-api-key": token };
}

registerOTel({
  serviceName: "mugline-web",
  traceExporter: new OTLPTraceExporter({
    url: `${SUPERLOG_ENDPOINT}/v1/traces`,
    headers: superlogHeaders(SUPERLOG_PUBLIC_TOKEN),
  }),
  // …same shape for log + metric exporters
});
```

Smoke checks should use tools already in the repo, e.g. `npm run typecheck` or
`npm run build`, plus a real app request where practical. Do not invent fragile
inline Node scripts that import TypeScript source files directly, and do not
assume `ts-node` exists unless it is already installed.

Source

Creator's repository · superloglabs/skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
What this skill can do
Reads your filesConnects to the internetRuns code on your machine
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk