ENG-9166: Suggestions for otel 1/3

Merged
MA
2mo ago
+916-156

Review follow-ups for #6899 (built-in trace points in reflex-base and the reflex-otel instrumentor), one finding per commit so each patch can be reviewed on its own. The findings come from a production-readiness pass over the stack: demo apps against OTLP/HTTP, OTLP/gRPC and console exporters, hostile event payloads, uploads, chained and background events, and the zero-code opentelemetry-instrument path.

What changes

Security and privacy

  • The client token no longer leaves the process. session.id is a truncated SHA-256 of the token, and token= is redacted from http.url / url.full / url.query on ASGI spans.
  • Only the W3C traceparent / tracestate strings are read from an event payload, through a dedicated propagator. Baggage or malformed fields sent by a client are ignored and never raise.

Span correctness

  • Events from the browser are CONSUMER spans, paired with the browser's PRODUCER span; chained events are INTERNAL children.
  • reflex.event.parent_txid is set only on chained events, and code.function.name is fully qualified.
  • Stream-delta handlers nest under the caller's span, and the events returned by the backend exception handler parent under the failed event's span.
  • Socket message sizes are measured without encoding a copy.

Configuration and defaults

  • The ASGI middleware uses the stable HTTP semantic conventions. OTEL_SEMCONV_STABILITY_OPT_IN=http is set at import time unless already set, before any contrib instrumentor reads it.
  • The mounted frontend's asset prefix is excluded from ASGI spans by default, an empty excluded-URLs variable is honoured, and so is OTEL_SDK_DISABLED=true.
  • ReflexInstrumentor().instrument() is self-sufficient: it is a no-op when already instrumented, so the app module needs no is_instrumented_by_opentelemetry guard, and it configures the SDK from OTEL_TRACES_EXPORTER / OTEL_METRICS_EXPORTER when no provider has been set.

Packaging

  • opentelemetry-api is a dependency of reflex-otel only. reflex-base imports nothing from opentelemetry until enable() runs, and enable() binds the API modules, tracer and propagator once, so no trace point carries an import statement or an assert.
  • The reflex-base floor is >= 0.9.10.post3.dev0, the smallest dev version above the newest published release. The release tooling lifts it onto the next reflex-base release automatically. The runtime dependency re-check (instrumentation_dependencies() and the instruments extra) is gone: reflex-base is a hard dependency and the resolver enforces the floor.
  • py.typed, __all__, reflex_otel registered as a logging package, README rewritten around the fixed behaviour.

Review notes

  • http.target needs no redaction: at the floor (0.49b0) and current ASGI instrumentation it holds the bare path; the query string only reaches http.url and url.query, which are redacted.
  • OTEL_SDK_DISABLED is read once at instrument() time, as the SDK's own providers read it once at construction. Toggling it in a running process is not a supported flow; uninstrument() then instrument() resets.

Follow-up work (out of scope here)

  • reflex.websocket.connections is an up/down counter and drifts if instrumentation is toggled while sockets are open (connections opened before instrument() are counted down but never up). An observable gauge over the live session map cannot drift.
  • Handler exception messages and stack traces are exported verbatim, which is standard OpenTelemetry behaviour. A record_exception switch on instrument() would let deployments opt out; the docs already say what leaves the process.
  • The reflex-base floor has to chase the 0.9.10 hotfix line (post1, post2 so far) until reflex-base ships a release from main, because the workspace versions itself as 0.9.10.post<distance>.dev0 and cannot express "the next minor". The min-deps job fails loudly if a published release ever satisfies the floor.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • This change requires a documentation update (the reflex-otel README is updated here; the docs page is updated in #7070)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them? (each commit message carries its rationale)
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally? (full unit suite, check_min_deps.py for reflex-base and reflex-otel)

🤖 Generated with Claude Code

https://claude.ai/code/session_01AsnJbeBU5w6piuxSJoz9gy