Skip to main content
Most Vercel projects call something that doesn’t run on Vercel: a backend API, a database, a queue, a payment provider. Vercel’s own trace view stops at the outgoing call. It shows one fetch span and how long it took, but nothing about what happened inside the other service. Vercel also can’t receive telemetry from services that run elsewhere. To see the whole request, both sides send their spans to Bronto, and every call from Vercel carries the W3C traceparent header. The other service continues that trace, and Bronto shows one trace from the Vercel edge to the database behind your backend.

When to use this

  • Your Vercel functions call an API, service or database that you run yourself somewhere else.
  • You want to see where the time goes in a slow request, including inside the other service.
  • You want logs from both sides to link to the same trace.
For telemetry that only covers Vercel itself, see Vercel Drains and OpenTelemetry in Vercel Functions.

How the trace is connected

Three things have to be true:
  1. The Vercel side records spans and sends them to Bronto, through a trace drain or an OTLP exporter in your code.
  2. Every outgoing call carries traceparent. It holds the trace ID and the ID of the calling span.
  3. The other service continues the trace and sends its spans to Bronto. Any OpenTelemetry SDK or agent reads traceparent from incoming requests automatically.

Step 1: Propagate trace context from Vercel

Next.js and other frameworks with @vercel/otel

@vercel/otel instruments fetch, but by default it adds traceparent only to your deployment’s own URLs. List the services you call in propagateContextUrls:
instrumentation.ts
With this in place, each fetch to https://api.example.com gets a client span and a traceparent header. The app’s spans share one trace with Vercel’s own spans for the request (Resolve Route, Invoke Middleware, Invoke Function).

Agents built with eve

eve takes OpenTelemetry instrumentations in agent/instrumentation/otel.ts. The undici instrumentation traces every fetch your tools make and adds traceparent to it:
agent/instrumentation/otel.ts
Each fetch from a tool then gets an HTTP client span, following the HTTP semantic conventions, under the tool’s execute_tool span, and the service it calls continues the trace. Version 0.33 matches the OpenTelemetry JS 2.x packages that @vercel/otel 2 uses. The instrumentation adds traceparent to every outgoing request; to leave some out, for example calls to third-party APIs, return true for them from its ignoreRequestHook option.

Other HTTP clients and workers

For calls that don’t go through fetch, use the OpenTelemetry instrumentation for that library, if there is one: the OpenTelemetry registry lists them. Otherwise, add the trace context of the active span to the request’s headers with the OpenTelemetry API:
With eve agents, each agent turn starts a new trace. A conversation of three turns is three traces. Tool calls inside a turn carry that turn’s trace ID, so the turn and everything its tools call stay together.

Step 2: Send spans from the other service

Instrument the other service with OpenTelemetry and point it at Bronto. Every OpenTelemetry SDK and agent continues an incoming traceparent without extra configuration. The standard environment variables:
For example, a Java service with the OpenTelemetry Java agent needs no code changes:
If the service runs several components (a reverse proxy, a database, the host), an OpenTelemetry Collector next to it can receive their OTLP data, read their log files, and forward everything to Bronto with one API key. Proxies such as Caddy and Envoy can create spans themselves, so the proxy hop appears in the trace too. See Send Traces to Bronto for SDK examples in other languages.

Step 3: Check the trace in Bronto

Send a request through your Vercel app, then open Explore Traces and search for the trace. A connected trace shows the Vercel spans and the other service’s spans in one waterfall. This one comes from the example lab: one agent turn on Vercel whose tool call goes through a proxy into a Java API and its database on AWS:
Bronto trace waterfall: invoke_agent petclinic-agent (393 ms) with an agent.step containing execute_tool find_owner (13 ms), its GET client span (10.8 ms), a caddy span (4.74 ms), the petclinic server span GET /petclinic/api/owners (4.35 ms), and below it a repository call and five PostgreSQL statements of 133 to 469 µs.

The same trace in Explore Traces: 17 spans from 3 services, from the agent turn down to the SQL statements.

If the other service’s spans appear in a separate trace, its incoming requests don’t carry traceparent: check propagateContextUrls (Step 1), and check that no proxy in between strips the header. To link logs to the trace, log the trace ID. Vercel’s log drain does this for you: runtime log lines from a traced request carry traceId and spanId. In the other service, OpenTelemetry log bridges and the Collector’s json_parser (with a trace setting) set the trace context on log records.

What you’ll see that Vercel can’t show

  • Network time to the other service. The client span on the Vercel side minus the server span on the other side is the time spent between them. In one test, a call from a function in iad1 to a backend in eu-west-1 took 313 ms, of which the backend needed 26 ms. Moving the function to dub1 brought the call down to 23 ms.
  • Where a slow request spends its time, down to individual database statements.
  • Errors that the Vercel side hides, for example a 400 from the backend that an agent turns into a friendly “I couldn’t do that”.

Known limits

  • Vercel’s trace view (vercel traces get <request-id> or the dashboard) shows the outgoing call as a single span. It doesn’t receive spans from other services.
  • eve starts a trace per agent turn, and Vercel’s infrastructure spans for eve’s requests form their own traces. Within a turn, the trace stays connected.
  • AI Gateway continues the caller’s trace (the gateway’s spans carry the same trace ID) when you also have an AI Gateway trace drain. The gateway’s root span refers to a parent span that isn’t exported, so it shows as a second root in the same trace.

Example

petclinic-agent is a runnable lab: an eve agent and a Next.js app on Vercel calling Spring PetClinic, which runs with Caddy, Postgres and an OpenTelemetry Collector on any Docker host outside Vercel (the example trace above came from AWS). It sends everything to Bronto (or any OTLP backend) and shows one trace from the agent’s tool call to the SQL statement.