Skip to content

OpenTelemetry tracing and metrics

Instrument any Store with OpenTelemetry spans and metrics.

"""OpenTelemetry tracing and metrics — Instrument any Store with OpenTelemetry spans and metrics.

Requires: pip install "remote-store[otel]" opentelemetry-sdk

Demonstrates:
- Wrapping a Store with OTel spans and metrics
- Using otel_observe() one-liner
- Using otel_hooks() for custom composition
- Viewing collected spans and metrics

---
see_also:
  - label: Observe
    url: ../../guides/observe.md
    note: instrumentation guide
"""

from __future__ import annotations

try:
    from opentelemetry.sdk.metrics import MeterProvider
    from opentelemetry.sdk.metrics.export import InMemoryMetricReader
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import SimpleSpanProcessor
    from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
except ImportError as _exc:
    print("This example requires: pip install 'remote-store[otel]' opentelemetry-sdk")
    raise SystemExit(1) from _exc

from opentelemetry import metrics, trace

from remote_store import Store
from remote_store.backends import MemoryBackend
from remote_store.ext.otel import otel_observe


def demo(observed: Store) -> None:
    """Store operations under OTel instrumentation."""
    observed.write("data/report.csv", b"id,value\n1,100\n2,200")
    print("Wrote data/report.csv")

    content = observed.read_bytes("data/report.csv")
    print(f"Read {len(content)} bytes from data/report.csv")

    observed.copy("data/report.csv", "data/report_backup.csv")
    print("Copied to data/report_backup.csv")

    observed.exists("data/report.csv")
    print("Checked existence")

    observed.delete("data/report_backup.csv")
    print("Deleted backup\n")


def main() -> None:
    # -- Set up OTel SDK with in-memory exporters (for demo) --------
    span_exporter = InMemorySpanExporter()
    tracer_provider = TracerProvider()
    tracer_provider.add_span_processor(SimpleSpanProcessor(span_exporter))
    trace.set_tracer_provider(tracer_provider)

    metric_reader = InMemoryMetricReader()
    meter_provider = MeterProvider(metric_readers=[metric_reader])
    metrics.set_meter_provider(meter_provider)

    # -- Create and instrument a Store ------------------------------
    store = Store(backend=MemoryBackend())
    observed = otel_observe(store)
    print("Store instrumented with OpenTelemetry\n")

    # -- Perform operations -----------------------------------------
    demo(observed)

    # -- Inspect collected spans ------------------------------------
    spans = span_exporter.get_finished_spans()
    print(f"--- {len(spans)} spans collected ---")
    for span in spans:
        attrs = dict(span.attributes or {})
        status = "OK" if span.status.is_ok else "ERROR"
        print(f"  {span.name:24s}  status={status:5s}  backend={attrs.get('remote_store.backend', '?')}")

    # -- Inspect collected metrics ----------------------------------
    data = metric_reader.get_metrics_data()
    assert data is not None
    print("\n--- Metrics ---")
    for rm in data.resource_metrics:
        for sm in rm.scope_metrics:
            for m in sm.metrics:
                points = list(m.data.data_points)
                total = sum(getattr(p, "value", 0) or getattr(p, "count", 0) for p in points)
                print(f"  {m.name:40s}  unit={m.unit:3s}  data_points={len(points)}  total={total}")

    # -- Cleanup ----------------------------------------------------
    store.close()
    tracer_provider.shutdown()
    meter_provider.shutdown()
    print("\nDone!")


if __name__ == "__main__":
    main()

See also