Skip to content

feat(core): OTLP trace exporter#7

Merged
ephor merged 3 commits into
mainfrom
feat/otlp-trace-exporter
May 27, 2026
Merged

feat(core): OTLP trace exporter#7
ephor merged 3 commits into
mainfrom
feat/otlp-trace-exporter

Conversation

@ephor

@ephor ephor commented May 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a generic trace-exporter mechanism to @getvision/core: a TraceExporter interface plus VisionServerOptions.exporters. Completed traces fan out in completeTrace (each exporter isolated — a failing sink never affects request handling) and are flushed on stop().
  • Ships OtlpTraceExporter — OTLP/JSON over HTTP with batching. Works with any OTLP-compatible backend (BetterStack, Honeycomb, Grafana Tempo, Datadog, an OTel Collector); the destination is purely endpoint + headers.
  • Maps Vision's model to OTLP: each HTTP request becomes a synthetic root SERVER span, c.span(...) calls become nested INTERNAL spans, trace logs become span events. Span/trace ids are random and spec-compliant via Web Crypto (no node: import, no hashing — cross-runtime).
  • @getvision/server: forwards vision.exporters into VisionCore and re-exports OtlpTraceExporter.
  • Docs: package README + docs-site config/section; roadmap marks "OpenTelemetry export" as shipped.

Usage

import { createVision, OtlpTraceExporter } from '@getvision/server'

createVision({
  service: { name: 'my-api' },
  vision: {
    exporters: [
      new OtlpTraceExporter({
        endpoint: 'https://<host>/v1/traces',
        headers: { Authorization: 'Bearer <token>' },
        serviceName: 'my-api',
      }),
    ],
  },
})

Test plan

  • @getvision/core unit tests for the Trace → OTLP transform (id format, span reparenting, status mapping, logs-as-events, payload shape) — 11/11 pass
  • @getvision/server API-contract test (exporter reachable + config accepts TraceExporter[])
  • bun test / bun run typecheck:test in CI (server tests can't run in the dev sandbox — deps not installed)
  • Manual smoke: point endpoint at a real OTLP backend and confirm traces arrive

ephor added 3 commits May 10, 2026 09:37
Adds a regression test asserting that createModule + Eden Treaty
preserves per-route body/query schemas. Catches both failure modes
observed pre-1.0.2 against @getvision/server@1.0.1:

- "stomp" (every depth-1 POST resolves to one shared body shape) —
  caught via cross-route distinctness assertion.
- intersection-merge (each route body becomes BodyA & BodyB & ...) —
  caught via per-route Equal<> probe.

The contract is enforced at type level — `bun run typecheck:test` fails
to compile if the prefix generic on createModule is ever removed.
Verified by temporarily reverting <const Prefix extends string> and
observing 8 type errors, then restoring.

Test infra wiring:
- new test/test:watch/typecheck:test scripts in @getvision/server
- separate tsconfig.test.json so production build still excludes tests
- @elysia/eden added as devDependency for the regression assertions

No source changes — fix already shipped in 1.0.2 (commit 002b468). This
is purely defensive scaffolding to keep the contract from regressing.
Add a generic exporter mechanism that fans completed traces out to OTLP/HTTP
backends (BetterStack, Honeycomb, Grafana Tempo, an OTel Collector, …). The
destination is purely endpoint + headers, so one exporter targets any
OTLP-compatible backend.

- core: `TraceExporter` interface + `VisionServerOptions.exporters`; fan-out in
  `completeTrace` (isolated per exporter) and flush on `stop()`
- core: `OtlpTraceExporter` (OTLP/JSON over HTTP) with batching, random
  spec-compliant ids via Web Crypto, and Trace→OTLP transform (synthetic root
  SERVER span + nested INTERNAL spans, logs as span events)
- server: forward `vision.exporters` into VisionCore; re-export OtlpTraceExporter
- docs: README + docs-site config/section; mark OpenTelemetry export shipped
Address review feedback on the new OTLP exporter:

- `maxQueueSize` (default 2048) is now a true hard cap — excess traces
  are dropped and reported via `onError` so the queue can't grow without
  bound when the backend is slow or down. Flush threshold is split out
  as `maxExportBatchSize` (default 512), matching the OTel SDK shape.
- Failed batches are re-buffered for the next flush (respecting the
  cap) instead of silently dropped on a single 5xx/network error.
- A `flushing` guard serializes concurrent flush() calls so the
  interval + threshold triggers can't stack N in-flight POSTs against
  the same endpoint.
- Headers merge case-insensitively, so a user-supplied `Content-Type`
  actually overrides the default rather than producing both keys.
- `onError` defaults to `console.warn` (was a silent no-op) and
  `VisionCore.exportTrace` warns on thrown exporters, so silent
  failures don't make local debugging impossible.
- Public surface narrowed: the exporters barrel exports only
  `TraceExporter`, `OtlpTraceExporter`, and `OtlpExporterOptions`.
- Tightened `transform.ts`: comment on the `error === true` contract
  with `createSpanHelper`; dropped the dead `?? newSpanId()` fallback.

Adds behavioral tests covering overflow drop, eager flush, re-buffer
on 5xx, serialized flush, and case-insensitive header merge.
@ephor ephor merged commit 7277446 into main May 27, 2026
8 checks passed
@ephor ephor deleted the feat/otlp-trace-exporter branch May 27, 2026 19:01
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