# Observability

Production systems need to answer three questions: *what happened*, *how long
did it take*, and *how is the system behaving over time*. Effect answers all
three out of the box, with no third-party instrumentation library required:

- **Logging** — structured, level-filtered log records carrying contextual
  annotations and timing spans.
- **Tracing** — spans that nest automatically along your effect's call graph,
  ready to export to any OpenTelemetry backend.
- **Metrics** — counters, gauges, histograms, and frequencies that aggregate in
  a process-wide registry.

Because all three are part of the `Effect` runtime, they share the same
machinery. A logger can emit log records *as span events*, a span's timing
feeds your traces, and metrics see the same fiber context as everything else.
You instrument your code once and choose where the data goes by providing a
`Layer` at the edge of your application.

```ts
import { Effect, Layer, Logger, Metric } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import { OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/unstable/observability"

// A counter, a span, and a structured log line — all from the same effect.
const requests = Metric.counter("http_requests_total")

const handleRequest = Effect.fn("handleRequest")(
  function*(route: string) {
    yield* Metric.update(requests, 1)
    yield* Effect.logInfo("handling request", { route })
  },
  // Pass cross-cutting combinators as trailing args to `Effect.fn` rather
  // than chaining them with `.pipe`.
  Effect.withSpan("handle-request")
)

// Choose destinations by providing layers at the edge of the app.
const Observability = Layer.mergeAll(
  Logger.layer([Logger.consoleJson]),
  OtlpTracer.layer({ url: "http://localhost:4318/v1/traces" }),
  OtlpLogger.layer({ url: "http://localhost:4318/v1/logs" })
).pipe(
  Layer.provide(OtlpSerialization.layerJson),
  Layer.provide(FetchHttpClient.layer)
)
```

The same `handleRequest` effect produces a metric, a trace span, and a log line.
Swapping `Observability` for a different layer (or omitting it) changes where
that telemetry goes without touching the business logic.

## In this section

- **[Logging](https://effect.plants.sh/observability/logging/)** — emit structured log records, filter
  by level, annotate with context, and plug in custom or batched loggers.
- **[Tracing](https://effect.plants.sh/observability/tracing/)** — create spans that nest along your
  call graph, annotate them, and export traces over OTLP.
- **[Metrics](https://effect.plants.sh/observability/metrics/)** — track counters, gauges, histograms,
  and frequencies with the built-in `Metric` registry.

## Exporting telemetry

For new projects, the lightweight modules under
`effect/unstable/observability` (`OtlpTracer`, `OtlpLogger`, `OtlpMetrics`,
`PrometheusMetrics`) speak OTLP and Prometheus directly with no extra runtime
dependencies. If you already run an OpenTelemetry SDK, the
`@effect/opentelemetry` integration wires Effect's tracer, logger, and metrics
into your existing pipeline instead.