@spilne/perfect-otel
v0.6.0
Published
OpenTelemetry bridge for @spilne/perfect-core observability — Tracer spans and metrics export.
Readme
@spilne/perfect-otel
OpenTelemetry bridge for @spilne/perfect-core observability. Two pieces:
OtelTracer implements core's Tracer service on top of an OTel tracer, so
every withSpan in your program becomes a real OTel span (with correct
parentage); OtelMetricsExporter pushes MetricsRegistry snapshots into
OTel instruments.
This is the general bridge — it knows nothing about HTTP. For HTTP-specific
client tracing (CLIENT spans per request, traceparent propagation on
@spilne/perfect-http), use @spilne/perfect-http-otel.
Install
bun add @spilne/perfect-otel @opentelemetry/apiQuickstart
Tracing
import { trace } from "@opentelemetry/api";
import { provide, run, succeed, withSpan, Tracer } from "@spilne/perfect-core";
import { OtelTracer } from "@spilne/perfect-otel";
const program = withSpan(
succeed(21).map((n) => n * 2),
"handle",
{ "app.tenant": "acme" },
);
const tracer = new OtelTracer(trace.getTracer("my-app"));
await run(provide(program, Tracer, tracer));Span parentage carries over: core's withSpan passes the enclosing Perfect
span as the parent, and the bridge maps it back to its OTel span — traces
nest identically in both worlds. Typed failures set span status to ERROR and
record the exception; interruption sets ERROR with a perfect.interrupted
attribute. When no tracer is provided, withSpan is zero-cost.
Metrics
import { metrics } from "@opentelemetry/api";
import { defaultMetricsRegistry } from "@spilne/perfect-core";
import { OtelMetricsExporter } from "@spilne/perfect-otel";
const exporter = new OtelMetricsExporter(metrics.getMeter("my-app"), defaultMetricsRegistry);
// Call on your export cadence — a Stream.tick loop, a
// PeriodicExportingMetricReader callback, a shutdown hook.
exporter.export();Counters are exported as deltas between calls, so repeated exports don't
double-count; gauges are absolute. export() is safe to call repeatedly and
returns the snapshot it exported.
Histogram values don't wait for export(): each value is forwarded to OTel
as it is recorded, with the same bucket boundaries, so OTel sees the real
distribution. Values recorded before the exporter existed are not forwarded.
Call exporter.close() to stop.
Features
OtelTracer— coreTracerservice backed by any@opentelemetry/apitracer; parentage, error status, exception recording, interruption markingOtelMetricsExporter—Counter/Gauge/Histogramfrom core'sMetricsRegistrymapped to OTel instruments, labels preserved, delta-correct on repeat export, exact histogram values
Links
- Repo: https://github.com/spilne/perfect
- Core observability APIs (
Log,withSpan,Metrics):documentation/15-observability.md - HTTP client tracing:
@spilne/perfect-http-otel
