Observability
std/observability is the friendly API for user-space spans, logs, metrics,
and structured events. Configure routing once at runtime, then emit observations
without choosing a wire format at every call site.
Use the short alias import { obs } from "observability", or the explicit
stdlib path import { obs } from "std/observability".
import { obs } from "std/observability"
pipeline default() {
let o = obs()
o.configure({backend: o.Backend.auto})
return o.span("plan_review", {pr_number: 1915}, { ->
o.log("starting review", "info", {phase: "start"})
let result = {duration_ms: 42, status: "ok"}
o.metric("review_duration_ms", result.duration_ms, {unit: "ms"})
return result
})
}
Backends
o.Backend.auto selects a backend from the process environment:
OTEL_EXPORTER_OTLP_ENDPOINTorHARN_OTEL_ENDPOINT: OTel OTLP-shaped payloads.SPLUNK_HEC_TOKEN: Splunk HEC-shaped JSON.HONEYCOMB_API_KEY: Honeycomb-style flat events.- Otherwise: human-readable
pretty_stderr.
Explicit backends:
import { obs } from "std/observability"
let B = obs().Backend
obs().configure({backend: B.otel(env("OTEL_EXPORTER_OTLP_ENDPOINT"))})
obs().configure({backend: B.splunk_hec(env("SPLUNK_HEC_ENDPOINT"), env("SPLUNK_HEC_TOKEN"))})
obs().configure({backend: B.honeycomb(env("HONEYCOMB_API_KEY"), "harn")})
obs().configure({backend: B.pretty_stderr})
obs().configure({backend: B.compose([B.otel("http://collector:4318"), B.pretty_stderr])})
Routing
Routes dispatch by kind, level, or default to a named backend:
import { obs } from "std/observability"
let o = obs()
let B = o.Backend
o.configure({
backends: {
otel: B.otel("http://collector:4318"),
splunk: B.splunk_hec("https://splunk.example/services/collector", env("SPLUNK_HEC_TOKEN")),
honeycomb: B.honeycomb(env("HONEYCOMB_API_KEY"), "harn"),
},
routes: [
{level: "error", backend: "splunk"},
{kind: "metric", backend: "honeycomb"},
{default: "otel"},
],
})
Span attributes merge into log and metric fields while the span is active, so
correlated events carry the same trace_id, span_id, and attribute bag.
Processors
Processors run after span/request/tenant enrichment and before backend routing. Use the stock redaction processor when logs or spans can carry credentials:
import { obs } from "std/observability"
let o = obs()
o.configure({
backend: o.Backend.otel("http://collector:4318"),
processors: [o.Processor.redaction],
})
redaction applies the active runtime redaction policy to the entire event, so
OTLP, Splunk, Honeycomb, pretty, and test backends all receive the same scrubbed
payload.