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.
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
Section titled “In this section”- Logging — emit structured log records, filter by level, annotate with context, and plug in custom or batched loggers.
- Tracing — create spans that nest along your call graph, annotate them, and export traces over OTLP.
- Metrics — track counters, gauges, histograms,
and frequencies with the built-in
Metricregistry.
Exporting telemetry
Section titled “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.