AI SDK calls emit OpenTelemetry spans once you register the integration. Without it you are flying blind on latency, cost, and errors.

Setup:

pnpm add @ai-sdk/otel

// instrumentation.ts (Next.js, project root)
import { registerOTel } from '@vercel/otel';
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';

export function register() {
  registerOTel({ serviceName: 'my-ai-app' });
  registerTelemetry(new OpenTelemetry());
}

// plain Node: call registerTelemetry(new OpenTelemetry()) at entry startup

Per-call metadata:

const result = await generateText({
  model: 'your-model-id',
  prompt: 'Write a haiku.',
  telemetry: { functionId: 'haiku-writer' },
});

Rules:
1. Telemetry is opt-out once registered: every call emits spans. Disable per call with telemetry: { isEnabled: false }, or globally by not registering.
2. Inputs and outputs are recorded by default. Set recordInputs/recordOutputs false when prompts contain PII, secrets, or large blobs. This is a privacy control, not just a cost one.
3. Use functionId to attribute spans to features (checkout-helper, support-draft). Cost dashboards group by it.
4. For call-site-specific logic (custom log lines, per-request billing math), use the lifecycle callbacks onStart/onEnd on the generateText/streamText call. They receive callId, usage, finishReason. Keep them fast; a throwing callback is swallowed but a slow one delays your response.
5. Correlate with your request tracing: pass request/user/tenant ids via runtimeContext so spans join the right trace.
6. Verify in your tracing backend: fire a test generation and confirm spans arrive with the functionId before shipping to production.