Skip to content

ENG-9166 feat(otel): built-in OpenTelemetry trace points, metrics and the reflex-otel instrumentor (1/3) - #6899

Open
FarhanAliRaza wants to merge 8 commits into
mainfrom
farhan/eng-9166-reflex-otel
Open

ENG-9166 feat(otel): built-in OpenTelemetry trace points, metrics and the reflex-otel instrumentor (1/3)#6899
FarhanAliRaza wants to merge 8 commits into
mainfrom
farhan/eng-9166-reflex-otel

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Part of #6227 (ENG-9166). Stack: 1/3 (this) → 2/3 compile spans + log correlation → 3/3 browser plugin.

What

Makes the framework instrumentation-aware but zero-cost without the instrumentation package:

  • reflex-base gets a hard dependency on opentelemetry-api (>=1.30). All trace points live in reflex_base/otel.py behind a module-level enabled bool — one attribute read per event when off, no OTel objects created.
  • New package packages/reflex-otel (reflex_otel.ReflexInstrumentor, a BaseInstrumentor with the standard opentelemetry_instrumentor entry point) flips the flag.

When enabled:

  • One span per event handler run: SERVER for events from the frontend (new trace, or child of the browser span if the event carries a traceparent field), INTERNAL for chained events, parented under the span that enqueued them via a context snapshot in EventContext.fork(). Attributes: reflex.event.name/txid/parent_txid/background, session.id, code.function.name; exceptions recorded on the span. Events are never parented under the websocket span.
  • The ASGI app is wrapped in the contrib ASGI middleware (/ping excluded, per-message websocket spans off).
  • Metrics: reflex.event.duration (+error.type), reflex.state.acquire.duration, reflex.websocket.message.size, reflex.websocket.connections.

Design

Follows the pattern used by Shiny for Python and pydantic-ai (first-party trace points + opentelemetry-api no-op) rather than wrapt monkeypatching. Survey of other frameworks and the measured no-op costs are in the ticket.

Notes

  • reflex-otel pins reflex-base >= <workspace dev version> like the other workspace packages; re-pin at the next release (check_min_deps --check-dev-pins is the publish gate).
  • Registered in publish.yml, dispatch_release.yml, detect.sh.
  • Verified end to end with Jaeger (traces) and Prometheus (metrics) on a demo app.

Tests

tests/units/reflex_base/test_otel.py, tests/units/reflex_otel/, otel cases in test_app.py, test_event_processor.py, test_base_state_processor.py; shared otel_sdk fixture in tests/units/conftest.py.

Review in cubic

@FarhanAliRaza
FarhanAliRaza requested a review from a team as a code owner August 17, 2026 20:57
@linear-code

linear-code Bot commented Aug 17, 2026

Copy link
Copy Markdown

ENG-9166

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2435892c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/reflex-otel/pyproject.toml
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds built-in OpenTelemetry event tracing and runtime metrics, plus a separately installable instrumentor that activates the trace points and wraps the generated ASGI application.

  • Adds event-span propagation, state-acquisition timing, and WebSocket metrics to the Reflex runtime.
  • Introduces the reflex-otel package and OpenTelemetry auto-instrumentation entry point.
  • Registers the new package in workspace and release automation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/otel.py Defines the opt-in tracing and metric instruments, context propagation, ASGI middleware factory, and enable/disable lifecycle.
packages/reflex-otel/src/reflex_otel/init.py Implements the OpenTelemetry instrumentor and configures the ASGI middleware and framework-native instruments.
reflex/app.py Installs the configured ASGI wrapper and records Socket.IO message sizes and connection counts.
packages/reflex-base/src/reflex_base/event/processor/event_processor.py Wraps enabled event-handler execution in OpenTelemetry spans while retaining the disabled fast path.
packages/reflex-base/src/reflex_base/event/processor/base_state_processor.py Records state-acquisition duration and restores captured context for exception-handler event chains.
packages/reflex-base/src/reflex_base/event/context.py Captures the active OpenTelemetry context when forking chained event contexts.
packages/reflex-otel/README.md Documents setup, emitted telemetry, configuration options, and the supported instrumentation lifecycle.
.github/workflows/dispatch_release.yml Adds reflex-otel as a selectable package in manually dispatched releases.
.github/workflows/publish.yml Adds reflex-otel to the shared package publishing workflow.

Reviews (5): Last reviewed commit: "fix(otel): parse excluded_urls for the A..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/otel.py
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/eng-9166-reflex-otel branch from b243589 to 54ec281 Compare August 17, 2026 21:03
@codspeed-hq

codspeed-hq Bot commented Aug 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing farhan/eng-9166-reflex-otel (2f9b04d) with main (b1601ab)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/reflex-otel/src/reflex_otel/__init__.py
Comment thread packages/reflex-otel/src/reflex_otel/__init__.py Outdated
Comment thread reflex/app.py
Comment thread packages/reflex-otel/README.md Outdated
Comment thread packages/reflex-otel/CHANGELOG.md Outdated
Comment thread tests/units/reflex_base/test_otel.py

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/units/reflex_otel/test_init.py">

<violation number="1" location="tests/units/reflex_otel/test_init.py:61">
P3: The first parametrize case `(None, True)` is non-deterministic: when `excluded_urls` is omitted, `ReflexInstrumentor._instrument` falls back to `os.environ.get("OTEL_PYTHON_REFLEX_EXCLUDED_URLS") or os.environ.get("OTEL_PYTHON_EXCLUDED_URLS") or "/ping"`. If either env var is set in the user's or CI environment, `/ping` will not be the effective excluded URL and the assertion `url_disabled("/ping") is True` fails. Clear or monkeypatch both env vars (e.g. `monkeypatch.delenv(..., raising=False)`) around the `instrument()` call to make the test hermetic.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread tests/units/reflex_otel/test_init.py

@Alek99 Alek99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local end-to-end OTel review: browser/backend propagation works on the current lockfile, but these package/runtime issues remain.

Comment thread packages/reflex-otel/src/reflex_otel/__init__.py
Comment thread packages/reflex-base/src/reflex_base/otel.py Outdated
…ion inside the span

opentelemetry-instrumentation-asgi < 0.56b0 stores excluded_urls verbatim
and calls .url_disabled() on it, so the default "/ping" string made every
request raise AttributeError. Parse with parse_excluded_urls first.

Move the event-duration histogram record inside the event span so exporter
latency is excluded from the sample and exemplars keep the trace/span IDs.
@FarhanAliRaza
FarhanAliRaza requested a review from Alek99 August 18, 2026 16:53

@Alek99 Alek99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working for me tried on flexgen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants