merton.obs

OpenTelemetry observability for merton.

Wraps the heavy calibration / panel / portfolio entry points with OpenTelemetry spans so production users get end-to-end traces in their existing OTel pipeline (Datadog, Honeycomb, Grafana Tempo, …).

The module is lazy — calling enable() is what wires up the TracerProvider. Before that, traced() is a no-op decorator, so import merton carries zero observability overhead for users who haven’t opted in.

Install with:

pip install "merton[obs]"

then:

import merton

merton.obs.enable(service_name="risk-engine", otlp_endpoint="http://otel-collector:4317")
# All MertonModel.fit / batch_fit / calibrator calls now emit spans.

Configuration via environment variables:

  • MERTON_OBS=1 — auto-enable on import (uses defaults).

  • MERTON_OTLP_ENDPOINT — override the default OTLP gRPC endpoint.

  • MERTON_OBS_CONSOLE=1 — also export to stdout (debugging).

Functions

is_enabled(→ bool)

Return True if OTel tracing is wired up in this process.

enable(→ None)

Initialise the OpenTelemetry TracerProvider.

disable(→ None)

Shut the current TracerProvider down. Tracing becomes a no-op again.

get_tracer()

Return the active tracer if enabled; None otherwise.

span(name, **attributes)

Context manager that opens a span when tracing is on.

traced(→ collections.abc.Callable[[F], F])

Decorator that wraps a function in a span when tracing is enabled.

Module Contents

merton.obs.is_enabled() bool[source]

Return True if OTel tracing is wired up in this process.

merton.obs.enable(*, service_name: str = _DEFAULT_SERVICE_NAME, otlp_endpoint: str | None = None, console: bool = False, insecure: bool = True) None[source]

Initialise the OpenTelemetry TracerProvider.

Idempotent — calling multiple times keeps the first configuration. Raises ImportError if merton[obs] isn’t installed.

merton.obs.disable() None[source]

Shut the current TracerProvider down. Tracing becomes a no-op again.

merton.obs.get_tracer()[source]

Return the active tracer if enabled; None otherwise.

merton.obs.span(name: str, **attributes: Any)[source]

Context manager that opens a span when tracing is on.

No-op (yields None) otherwise. Use this around any block of work you want measured:

from merton import obs

with obs.span("merton.fit", method="duan_mle"):
    result = model.fit(firm)
merton.obs.traced(name: str | None = None, **default_attributes: Any) collections.abc.Callable[[F], F][source]

Decorator that wraps a function in a span when tracing is enabled.

The decorated function pays a single is_enabled() check per call when tracing is off — well under a microsecond.