Skip to main content

observability

Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.

Source facts

Repository
openai/plugins
Last source activity
April 21, 2026 at 18:07
Detected SKILL.md language
English
Stars
7,904
Forks
1,014

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
observability
description
Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.
metadata
{"priority":6,"docs":["https://vercel.com/docs/observability","https://vercel.com/docs/observability/otel-overview"],"sitemap":"https://vercel.com/sitemap/docs.xml","pathPatterns":["instrumentation.ts","instrumentation.js","src/instrumentation.ts","src/instrumentation.js","app/layout.*","src/app/layout.*","pages/_app.*","src/pages/_app.*","apps/*/instrumentation.ts","apps/*/instrumentation.js","apps/*/app/layout.*","apps/*/src/app/layout.*","apps/*/pages/_app.*","apps/*/src/pages/_app.*","sentry.client.config.*","sentry.server.config.*","sentry.edge.config.*"],"bashPatterns":["\\bvercel\\s+logs?\\b","\\bvercel\\s+logs?\\s+.*--follow\\b","\\bvercel\\s+logs?\\s+.*--level\\b","\\bvercel\\s+logs?\\s+.*--since\\b","\\bcurl\\s+.*deployments.*events\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/analytics\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/analytics\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@vercel/analytics\\b","\\byarn\\s+add\\s+[^\\n]*@vercel/analytics\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/speed-insights\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/speed-insights\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@vercel/speed-insights\\b","\\byarn\\s+add\\s+[^\\n]*@vercel/speed-insights\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@sentry/nextjs\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@sentry/nextjs\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@sentry/nextjs\\b","\\byarn\\s+add\\s+[^\\n]*@sentry/nextjs\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@sentry/node\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@sentry/node\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@sentry/node\\b","\\byarn\\s+add\\s+[^\\n]*@sentry/node\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@datadog/browser-rum\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@datadog/browser-rum\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@datadog/browser-rum\\b","\\byarn\\s+add\\s+[^\\n]*@datadog/browser-rum\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bcheckly\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bcheckly\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bcheckly\\b","\\byarn\\s+add\\s+[^\\n]*\\bcheckly\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bnewrelic\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bnewrelic\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bnewrelic\\b","[Truncated]"],"promptSignals":{"phrases":["add logging","add logs","set up logging","setup logging","configure logging","structured logging","log drain","log drains","vercel analytics","speed insights","web analytics","opentelemetry","otel","instrumentation","monitoring","set up monitoring","add observability","track errors","error tracking","sentry","datadog","check the logs","show me the error","what went wrong","where did it fail","show me the logs","find the error","why did it fail","debug the error"],"allOf":["[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]"],"anyOf":["logging","monitoring","analytics","observability","telemetry","traces","metrics","debug","debugging","stuck","hanging","hung","waiting","stalled","spinning","timeout","slow","pending","unresponsive"],"minScore":6}}
# Vercel Observability You are an expert in Vercel's observability stack — runtime logs, structured logging, Drains, Web Analytics, Speed Insights, and monitoring integrations. **Always start with logging.** When something is stuck, slow, or broken, the first step is always to check or add logs. ## Structured Logging Baseline Add this to every API route and server action as a minimum. If the user reports something stuck, hanging, or slow, verify this baseline exists first: ```ts const start = Date.now(); console.log(JSON.stringify({ level: "info", msg: "start", route: "/api/example", requestId: req.headers.get("x-vercel-id") })); // ... your logic ... console.log(JSON.stringify({ level: "info", msg: "done", route: "/api/example", ms: Date.now() - start })); // On error: console.error(JSON.stringify({ level: "error", msg: "failed", route: "/api/example", error: err.message, ms: Date.now() - start })); ``` ## Runtime Logs Vercel provides real-time logs for all function invocations. ### Structured Logging ```ts // app/api/process/route.ts export async function POST(req: Request) { const start = Date.now() const data = await req.json() // Structured logs appear in Vercel's log viewer console.log(JSON.stringify({ level: 'info', message: 'Processing request', requestId: req.headers.get('x-vercel-id'), payload_size: JSON.stringify(data).length, })) try { const result = await processData(data) console.log(JSON.stringify({ level: 'info', message: 'Request completed', duration_ms: Date.now() - start, })) return Response.json(result) } catch (error) { console.error(JSON.stringify({ level: 'error', message: 'Processing failed', error: error instanceof Error ? error.message : String(error), duration_ms: Date.now() - start, })) return Response.json({ error: 'Internal error' }, { status: 500 }) } } ``` ### Next.js Instrumentation ```ts // instrumentation.ts (Next.js 16) export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { // Initialize monitoring on server startup const { initMonitoring } = await import('./lib/monitoring') initMonitoring() } } ``` ### Runtime Logs via REST API Query deployment runtime logs programmatically. The endpoint returns `application/stream+json` — a streaming response where each line is a separate JSON object. ```bash # Stream runtime logs for a deployment (returns application/stream+json) curl -N -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v3/deployments/<deployment-id>/events" \ --max-time 120 ``` > **Streaming guidance:** The response is unbounded — always set a timeout (`--max-time` in curl, `AbortController` with `setTimeout` in fetch). Parse line-by-line as NDJSON. Each line contains `{ timestamp, text, level, source }`. ```ts // Programmatic streaming with timeout const controller = new AbortController() const timeout = setTimeout(() => controller.abort(), 60_000) // 60s max const res = await fetch( `https://api.vercel.com/v3/deployments/${deploymentId}/events`, { headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` }, signal: controller.signal, } ) const reader = res.body!.getReader() const decoder = new TextDecoder() let buffer = '' try { while (true) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) const lines = buffer.split('\n') buffer = lines.pop()! // keep incomplete line in buffer for (const line of lines) { if (!line.trim()) continue const event = JSON.parse(line) console.log(`[${event.level}] ${event.text}`) } } } finally { clearTimeout(timeout) } ``` > **MCP alternative:** Use `get_runtime_logs` via the Vercel MCP server for agent-friendly log queries without managing streams directly. See `⤳ skill: vercel-api`. ## Web Analytics Privacy-friendly, first-party analytics with no cookie banners required. ### Installation ```bash npm install @vercel/analytics ``` ### Setup (Next.js App Router) ```tsx // app/layout.tsx import { Analytics } from '@vercel/analytics/next' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html> <body> {children} <Analytics /> </body> </html> ) } ``` ### Custom Events (Pro/Enterprise) Track business-specific events beyond pageviews. ```ts import { track } from '@vercel/analytics' // Track a conversion track('purchase', { product: 'pro-plan', value: 20, currency: 'USD', }) // Track a feature usage track('feature_used', { name: 'ai-chat', duration_ms: 3200, }) ``` ### Server-Side Tracking ```ts import { track } from '@vercel/analytics/server' export async function POST(req: Request) { const data = await req.json() await processOrder(data) track('order_completed', { order_id: data.id, total: data.total, }) return Response.json({ success: true }) } ``` ## Speed Insights Real-user performance monitoring built on Core Web Vitals. ### Installation ```bash npm install @vercel/speed-insights ``` ### Setup (Next.js App Router) ```tsx // app/layout.tsx import { SpeedInsights } from '@vercel/speed-insights/next' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html> <body> {children} <SpeedInsights /> </body> </html> ) } ``` ### Metrics Tracked | Metric | What It Measures | Good Threshold | |--------|-----------------|----------------| | LCP | Largest Contentful Paint | < 2.5s | | INP | Interaction to Next Paint | < 200ms | | CLS | Cumulative Layout Shift | < 0.1 | | FCP | First Contentful Paint | < 1.8s | | TTFB | Time to First Byte | < 800ms | ### Performance Attribution Speed Insights attributes metrics to specific routes and pages, letting you identify which pages are slow and why. ## Drains Drains forward observability data from Vercel to external endpoints. They are the primary mechanism for exporting logs, traces, Speed Insights, and Web Analytics data to third-party platforms. > **Plan requirement:** Drains require a **Pro or Enterprise** plan. For Hobby plans, see the [Fallback Guidance](#fallback-guidance-no-drains) section below. ### Data Types Drains can forward multiple categories of telemetry: | Data Type | What It Contains | Use Case | |-----------|-----------------|----------| | **Logs** | Runtime function logs, build logs, static access logs | Centralized log aggregation | | **Traces** | OpenTelemetry-compatible distributed traces | End-to-end request tracing | | **Speed Insights** | Core Web Vitals and performance metrics | Performance monitoring pipelines | | **Web Analytics** | Pageviews, custom events, visitor data | Analytics data warehousing | ### Supported Formats | Format | Protocol | Best For | |--------|----------|----------| | JSON | HTTPS POST | Custom backends, generic log collectors | | NDJSON | HTTPS POST | Streaming-friendly consumers, high-volume pipelines | | Syslog | TLS syslog | Traditional log management (rsyslog, syslog-ng) | ### Setting Up Drains Drains are configured via the **Vercel Dashboard** at `https://vercel.com/dashboard/{team}/~/settings/log-drains` or the **REST API**. #### Via Dashboard 1. Open `https://vercel.com/dashboard/{team}/~/settings/log-drains` (replace `{team}` with your team slug) 2. Click **Add Log Drain** 3. Select the drain type (JSON, NDJSON, or syslog) and enter the endpoint URL 4. Choose which environments and sources to include 5. Click **Create** to activate the drain #### Via REST API (`/v1/drains`) ```bash # List all drains curl -s -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v1/drains?teamId=$TEAM_ID" | jq # Create a JSON drain curl -X POST -H "Authorization: Bearer $VERCEL_TOKEN" \ -H "Content-Type: application/json" \ "https://api.vercel.com/v1/drains?teamId=$TEAM_ID" \ -d '{ "url": "https://your-endpoint.example.com/logs", "type": "json", "sources": ["lambda", "edge", "static"], "environments": ["production"] }' # Test a drain (sends a test payload to your endpoint) curl -X POST -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v1/drains/<drain-id>/test?teamId=$TEAM_ID" # Update a drain (change URL, sources, or environments) curl -X PATCH -H "Authorization: Bearer $VERCEL_TOKEN" \ -H "Content-Type: application/json" \ "https://api.vercel.com/v1/drains/<drain-id>?teamId=$TEAM_ID" \ -d '{ "url": "https://new-endpoint.example.com/logs", "environments": ["production", "preview"] }' # Delete a drain curl -X DELETE -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v1/drains/<drain-id>?teamId=$TEAM_ID" ``` ### Web Analytics Drains Reference When a drain is configured to receive Web Analytics data, payloads arrive as batched events. The format depends on your drain type. #### JSON Payload Schema ```json [ { "type": "pageview", "url": "https://example.com/blog/post-1", "referrer": "https://google.com", "timestamp": 1709568000000, "geo": { "country": "US", "region": "CA", "city": "San Francisco" }, "device": { "os": "macOS", "browser": "Chrome", "isBot": false }, "projectId": "prj_xxxxx", "environment": "production" }, { "type": "custom_event", "name": "purchase", "url": "https://example.com/checkout", "properties": { "product": "pro-plan", "value": 20 }, "timestamp": 1709568100000, "geo": { "country": "US" }, "device": { "os": "macOS", "browser": "Chrome", "isBot": false }, "projectId": "prj_xxxxx", "environment": "production" } ] ``` #### NDJSON Payload Format Each line is a separate JSON object (one event per line): ``` {"type":"pageview","url":"https://example.com/","timestamp":1709568000000,"geo":{"country":"US"},"device":{"browser":"Chrome"},...}
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub