Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

reflex-otel

OpenTelemetry instrumentation for the Reflex framework.

from reflex_otel import ReflexInstrumentor

ReflexInstrumentor().instrument()

Nothing in the app module needs guarding: a second instrument() is a silent no-op, so the line above can run every time the module is imported.

Configure the SDK the way you prefer:

  • Set the standard variables and let instrument() do it. When OTEL_TRACES_EXPORTER or OTEL_METRICS_EXPORTER is set and no SDK provider has been installed yet, instrument() configures opentelemetry-sdk from the OTEL_* environment, exactly as opentelemetry-instrument would:

    OTEL_SERVICE_NAME=myapp OTEL_TRACES_EXPORTER=otlp OTEL_METRICS_EXPORTER=otlp \
      OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
      OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 reflex run
    

    Install opentelemetry-exporter-otlp-proto-http for this HTTP/protobuf configuration. Without the protocol setting, otlp defaults to gRPC and requires the separate gRPC exporter package.

  • Or build the providers yourself and pass them: ReflexInstrumentor().instrument(tracer_provider=provider, meter_provider=meter_provider). Do that from a module that is imported once (not the app module, which test harnesses may re-import) or the SDK warns about overriding providers.

  • Or use no code at all: the package registers an opentelemetry_instrumentor entry point, so opentelemetry-instrument reflex run enables it (the auto-instrumentation sitecustomize reaches the backend worker through the inherited PYTHONPATH).

What you get

Traces:

  • One span per event handler run, named after the event: CONSUMER for events sent by the frontend (a new trace, or a child of the browser's PRODUCER span when the event carries a traceparent field), INTERNAL for chained events, which are children of the span that enqueued them. Only string traceparent/tracestate fields are read from the event; they never reach the handler, and baggage or anything else a client sends is ignored. The sampled flag of a client traceparent is honoured by the SDK's default parent-based sampler, so a client decides whether its own events are recorded; use ParentBased(root=..., remote_parent_sampled=..., remote_parent_not_sampled=...) or OTEL_TRACES_SAMPLER=always_on to keep that decision on the server.
  • HTTP requests and the websocket connection are wrapped in the standard OpenTelemetry ASGI middleware (per-message websocket spans are off).
  • One reflex.compile span per app compile (reflex.compile.trigger, reflex.compile.dry_run) with the stages reflex.compile.evaluate_pages, .pages, .copy_assets, .install_frontend_packages, .write as child spans.

What leaves the process: event and handler names, a pseudonymous session.id (a truncated SHA-256 of the client token, never the token itself), exception types, messages and stack traces of failed handlers, and the ASGI middleware's request attributes with the token query parameter of the websocket URL redacted. Event payloads and state are never recorded.

Metrics:

Instrument Type Unit Attributes
reflex.event.duration histogram s reflex.event.name, reflex.event.background, error.type
reflex.state.acquire.duration histogram s reflex.event.name
reflex.websocket.message.size histogram By network.io.direction (transmit/receive); default sio only
reflex.websocket.connections up-down counter {connection}

Plus the ASGI middleware's http.server.* metrics. The instrumentor opts the middleware into the stable HTTP semantic conventions (OTEL_SEMCONV_STABILITY_OPT_IN=http) unless that variable is already set, so request attributes use the same generation of names as Reflex's own.

Browser (frontend) tracing

# rxconfig.py
from reflex_otel import OtelPlugin

config = rx.Config(
    app_name="myapp",
    plugins=[OtelPlugin(endpoint="https://collector.example.com/v1/traces")],
)

The plugin compiles a small OpenTelemetry web bundle into the frontend:

  • every event sent to the backend gets a PRODUCER span and a W3C traceparent, so the backend event span joins the browser trace (one trace per interaction, browser → backend → chained events);
  • web vitals (web_vital.LCP, CLS, INP, FCP, TTFB) as spans with web_vital.value / web_vital.rating;
  • with render_timing=True, React commits as react.render spans (react.render.phase, react.render.actual_duration_ms); this aliases react-dom/client to the react-dom/profiling build and emits one span per commit, so it is off by default;
  • socket.connect / socket.disconnect spans for reconnect tracking (unintentional disconnects are marked as errors).

Options: endpoint (OTLP/HTTP traces URL reachable from the browser; required to export, with no default and no OTEL_EXPORTER_OTLP_* fallback: without it no exporter is installed and browser spans are dropped), service_name (default <app_name>-frontend), headers (compiled into the public bundle — no secrets), web_vitals, render_timing. The endpoint must allow CORS from the app origin.

Options

instrument() accepts tracer_provider, meter_provider, excluded_urls (comma-separated URL patterns skipped by the ASGI middleware; defaults to OTEL_PYTHON_REFLEX_EXCLUDED_URLS, else OTEL_PYTHON_EXCLUDED_URLS, else /ping plus the compiled frontend's /assets/ when the backend serves it, as reflex run --env prod does on one port; pass "", or set the variable to an empty string, to exclude nothing) and the ASGI hooks server_request_hook, client_request_hook, client_response_hook. Call instrument() before the app is served: uninstrument() turns the framework trace points off again, but an ASGI middleware that was already installed stays until the process restarts.

Metadata

Release files for reflex-otel 0.2.0a1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for reflex-otel 0.2.0a1
File Size Uploaded
reflex_otel-0.2.0a1.tar.gz 12.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for reflex-otel 0.2.0a1
File Interpreter ABI Platform
reflex_otel-0.2.0a1-py3-none-any.whl Python 3 none any Details

Total release size: 26.7 kB

Release files / reflex_otel-0.2.0a1.tar.gz

Download URL reflex_otel-0.2.0a1.tar.gz
Size 12.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a4bc9cfa5975e15935eaa358866eaec6880ba44d8bd42629431bd0cdfefcf4f7
BLAKE2b-256 checksum
How to use checksums
1312802c1dfcf71e63353de99be20124eb5a4c3f5837f47759d0405d4ad1399f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / reflex_otel-0.2.0a1-py3-none-any.whl

Download URL reflex_otel-0.2.0a1-py3-none-any.whl
Size 14.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1c57cea0cfc46941c9b8f9e982ecd8423789924070b9549a4b11df1aa945e3f9
BLAKE2b-256 checksum
How to use checksums
bdea559a127db16b5e7d2dcd66bf0265a7d0071f20c3b83f45a1947b59bf7b16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page