# 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...

Website: https://harnlang.com/stdlib/observability.html

This page documents Harn, which is pre-1.0. Language, standard library, and CLI APIs may change. If the intended version is unclear, clarify before using this page.

---

`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"`.

```harn
import { obs } from "std/observability"

pipeline default(harness: Harness) {
  const o = obs()
  o.configure({backend: o.Backend.auto})

  return o.span("plan_review", {pr_number: 1915}, { ->
    o.log("starting review", "info", {phase: "start"})
    const 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:

1. `OTEL_EXPORTER_OTLP_ENDPOINT` or `HARN_OTEL_ENDPOINT`: OTel OTLP-shaped payloads.
2. `SPLUNK_HEC_TOKEN`: Splunk HEC-shaped JSON.
3. `HONEYCOMB_API_KEY`: Honeycomb-style flat events.
4. Otherwise: human-readable `pretty_stderr`.

Explicit backends:

```harn
import { obs } from "std/observability"

const B = obs().Backend

obs().configure({
  backend: B.otel(harness.env.get("OTEL_EXPORTER_OTLP_ENDPOINT"))
})
obs().configure({backend: B.splunk_hec(
  harness.env.get("SPLUNK_HEC_ENDPOINT"),
  harness.env.get("SPLUNK_HEC_TOKEN"),
)})
obs().configure({
  backend: B.honeycomb(harness.env.get("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:

```harn
import { obs } from "std/observability"

const o = obs()
const B = o.Backend

o.configure({
  backends: {
    otel: B.otel("http://collector:4318"),
    splunk: B.splunk_hec(
      "https://splunk.example/services/collector",
      harness.env.get("SPLUNK_HEC_TOKEN"),
    ),
    honeycomb: B.honeycomb(harness.env.get("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:

```harn
import { obs } from "std/observability"

const 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.

---

## Read next

- [Pipeline lifecycle presets](https://harnlang.com/stdlib/lifecycle.md)
- [Run-record observability outputs](https://harnlang.com/observability/run-record-outputs.md)
