Skip to content

Production browser span export: same-origin OTLP forwarded to Axiom#981

Merged
RhysSullivan merged 1 commit into
mainfrom
claude/prod-browser-traces
Jun 12, 2026
Merged

Production browser span export: same-origin OTLP forwarded to Axiom#981
RhysSullivan merged 1 commit into
mainfrom
claude/prod-browser-traces

Conversation

@RhysSullivan

@RhysSullivan RhysSullivan commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

The web client's OTLP exporter ships in the production build.

  • wrangler.jsonc bakes VITE_PUBLIC_OTLP_TRACES_URL=/v1/traces; the client resolves relative endpoints against the page origin (previews get tracing for free).
  • A new worker route (apps/cloud/src/observability/browser-traces.ts) forwards browser span batches to Axiom with the server-held token — the browser never sees AXIOM_TOKEN. It runs before the worker's server span (exporter traffic doesn't trace itself), requires a session cookie to be present, caps bodies at 2MB, accepts-and-drops when Axiom isn't configured, and never reflects the upstream response. Unit-tested.
  • Client hardening: browser-only install (the module is also evaluated during SSR) and per-SESSION sampling via VITE_PUBLIC_OTLP_SAMPLE_RATIO (a session traces everything or nothing — per-span sampling would shred waterfalls; currently 1).
  • vite dev strips the baked endpoint unless a local motel is listening, so plain bun dev doesn't post spans into a dead proxy every second.

No new deploy-time secrets — reuses the existing AXIOM_TOKEN. After deploy, "why was that page slow for this user" is answerable in Axiom with the same click→server→DB waterfall the e2e suite gets from motel locally.

Stack

  1. Developer-session e2e: chat-theater recordings, focus timeline, one-mp4 films #962
  2. E2e distributed tracing: suite-owned motel, browser OTLP, end-to-end trace join #979
  3. Runs viewer: live session player with URL bar, trace rail, self-hosted trace viewer #980
  4. Production browser span export: same-origin OTLP forwarded to Axiom #981 👈 current

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud d53d12f Jun 12 2026, 05:52 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing d53d12f Commit Preview URL

Branch Preview URL
Jun 12 2026, 05:50 PM

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@RhysSullivan RhysSullivan force-pushed the claude/e2e-session-player branch from eb848df to 8a221c7 Compare June 12, 2026 17:46
@pkg-pr-new

pkg-pr-new Bot commented Jun 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@981

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@981

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@981

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@981

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@981

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@981

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@981

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@981

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@981

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@981

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@981

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@981

executor

npm i https://pkg.pr.new/executor@981

commit: 31eb6cd

@RhysSullivan RhysSullivan force-pushed the claude/prod-browser-traces branch from 31eb6cd to 8c0d322 Compare June 12, 2026 17:46
@RhysSullivan RhysSullivan force-pushed the claude/e2e-session-player branch from 8a221c7 to e49bdd1 Compare June 12, 2026 17:48
@RhysSullivan RhysSullivan changed the base branch from claude/e2e-session-player to main June 12, 2026 17:48
@RhysSullivan RhysSullivan marked this pull request as ready for review June 12, 2026 17:48
The web client's OTLP exporter now ships in the production build:
wrangler.jsonc bakes VITE_PUBLIC_OTLP_TRACES_URL=/v1/traces, the client
resolves it against the page origin, and a new worker route
(observability/browser-traces.ts) forwards the batch to Axiom with the
server-held token — the browser never sees AXIOM_TOKEN. The route runs
before the worker's server span so exporter traffic doesn't trace
itself, requires a session cookie to be present, caps body size, and
never reflects Axiom's response to the caller.

Client hardening for prod: browser-only install (the module is also
evaluated during SSR), relative exporter URLs resolved against the
page origin, and per-SESSION sampling via
VITE_PUBLIC_OTLP_SAMPLE_RATIO (a session traces everything or nothing
— per-span sampling would shred waterfalls). vite dev strips the baked
endpoint unless a local motel is actually listening, so plain bun dev
doesn't post spans into a dead proxy. e2e keeps working unchanged
(verified: 27 browser entries harvested on a fresh run).
@RhysSullivan RhysSullivan force-pushed the claude/prod-browser-traces branch from 8c0d322 to d53d12f Compare June 12, 2026 17:49
@RhysSullivan RhysSullivan merged commit 2d86581 into main Jun 12, 2026
3 of 5 checks passed
RhysSullivan added a commit that referenced this pull request Jun 12, 2026
#985)

Every API/MCP request produced TWO identical sibling http.server spans:
the worker-boundary span server.ts opens (scope executor-cloud-worker)
AND the one Effect's HttpMiddleware.tracer opens for app-owned paths
(scope executor-cloud) — both joined the caller's traceparent, so the
waterfall showed a childless twin next to the real envelope, and server
span ingest was doubled (visible in Axiom: 39.7k executor-cloud vs
24.9k executor-cloud-worker spans on /mcp alone).

The worker now skips its span for app-owned paths (app-paths.ts: /api/*,
/mcp, /.well-known/*) — Effect's middleware already parses traceparent
and parents the workos/store/db children there. Non-app paths (Start
SSR, marketing proxy, /_astro assets) keep the worker envelope span,
and the flush-on-waitUntil still runs on both branches so batched
exports survive the isolate.

Verified against the suite motel: API scenario + browser scenario runs
now record exactly one http.server per trace (worker scope only on /
and SSR routes), browser→server join intact (executor-web client span
→ executor-cloud server span → workos/user_store/fumadb children);
cloud observability unit tests green.

Found via the prod Axiom dataset right after #981 made browser traces
land — the duplicate was previously invisible without the join.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant