Skip to content

ENG-10963 feat(log): add stdlib logging pipeline in reflex_base.utils.log (1/5) - #6863

Merged
FarhanAliRaza merged 11 commits into
mainfrom
farhan/eng-10963-log-1-pipeline
Aug 18, 2026
Merged

ENG-10963 feat(log): add stdlib logging pipeline in reflex_base.utils.log (1/5)#6863
FarhanAliRaza merged 11 commits into
mainfrom
farhan/eng-10963-log-1-pipeline

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Adds reflex_base.utils.log: a standard python logging pipeline with a rich-rendering console handler (legacy colors preserved), a JSON-lines handler behind REFLEX_LOG_JSON, record deduplication, and file logging.

  • LogLevel gains a correct total ordering (the str mixin compared alphabetically, so --loglevel critical printed the system-info banner) and to_logging_level().
  • console.set_log_level delegates into the pipeline; print/rule/status/progress respect JSON mode; print_table added.
  • import reflex bootstraps the reflex-owned loggers (sinks attach lazily on the first record).
  • No call sites are migrated yet — that happens in PRs 2-4.

Stack (ENG-10963)

this → #6864#6865#6866#6867.
Merge in order; each PR is based on the previous branch.

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

linear-code Bot commented Aug 10, 2026

Copy link
Copy Markdown

ENG-10963

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a standard-library logging pipeline with Rich, JSON-lines, deduplication, and file sinks, then routes existing console-level configuration through it.

  • Adds ordered log-level conversion to standard logging levels.
  • Bootstraps Reflex-owned logger namespaces and supports managed CLI configuration.
  • Makes console helpers, including table output, compatible with JSON mode.
  • Adds focused logging, environment, and import-performance coverage.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains from the previously reported issues.

The dotenv logger is now placed under the Reflex logging hierarchy during direct environment imports, and table output emits a structured JSON record in JSON mode; no blocking failure remains.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/utils/log.py Introduces the logging hierarchy, managed handlers, Rich and JSON formatting, deduplication, file output, and deprecation utilities without an eligible blocking issue.
packages/reflex-base/src/reflex_base/utils/console.py Delegates log-level state to the new pipeline and makes interactive output—including tables—respect JSON mode.
packages/reflex-base/src/reflex_base/environment.py Adds the JSON logging environment variable and routes dotenv dependency errors through a bootstrapped module logger.
packages/reflex-base/src/reflex_base/constants/base.py Implements rank-based total ordering for LogLevel and maps values to standard-library logging levels.
tests/units/reflex_base/utils/test_log.py Covers rendering, JSON serialization, level gating, deduplication, managed and library modes, bootstrap behavior, and lightweight imports.

Reviews (11): Last reviewed commit: "fix(log): never attach sinks from the im..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/environment.py
Comment thread packages/reflex-base/src/reflex_base/utils/console.py
@codspeed-hq

codspeed-hq Bot commented Aug 10, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
🆕 1 new benchmark
⏩ 8 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
🆕 Simulation test_import_reflex N/A 4.9 ms N/A

Comparing farhan/eng-10963-log-1-pipeline (c8998f0) with main (852ef91)

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.

@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: 3c9743d5da

ℹ️ 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-base/src/reflex_base/utils/log.py
Comment thread packages/reflex-base/src/reflex_base/utils/log.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/console.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.

All reported issues were addressed across 13 files

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

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/log.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/console.py
Comment thread reflex/__init__.py
Comment thread packages/reflex-base/src/reflex_base/utils/console.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/log.py
Comment thread packages/reflex-base/src/reflex_base/environment.py
Comment thread packages/reflex-base/src/reflex_base/utils/log.py

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

some legit reviewbot feedback here.

i'm going to skim the rest of the PRs to see if there's maybe some overlap i'm not seeing that will come together

Comment thread packages/reflex-base/src/reflex_base/utils/log.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/log.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/log.py
Comment thread packages/reflex-base/src/reflex_base/utils/log.py
Comment thread tests/units/reflex_base/utils/test_log.py Outdated
Comment thread tests/units/reflex_base/utils/test_log.py
Comment thread tests/units/reflex_base/utils/test_log.py
Comment thread tests/units/reflex_base/utils/test_log.py
Comment thread tests/units/conftest.py Outdated

@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.

All reported issues were addressed across 4 files (changes from recent commits).

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

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/log.py
Comment thread packages/reflex-base/src/reflex_base/utils/log.py Outdated
Comment thread packages/reflex-base/src/reflex_base/utils/log.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 8 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="packages/reflex-base/src/reflex_base/utils/log.py">

<violation number="1" location="packages/reflex-base/src/reflex_base/utils/log.py:428">
P2: When a caller supplies an extra field named `logger`, `message`, `timestamp`, or `pid`, `emit_json_print` overwrites the canonical JSON metadata because `**fields` is expanded last. Filter reserved keys or expand extras before the canonical fields.</violation>
</file>

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

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/log.py
@masenf

masenf commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator
  1. import reflex is twice as slow on my machine now (claude claims 35x slower, but i don't see it)

reflex/init.py now eagerly imports reflex_base.utils.log to call bootstrap(), which drags in rich.console (26 ms), reflex_base.constants (29 ms), logging, json, shutil, pathlib, typing:

main: 0.0014 s 58 modules rich_loaded=False
PR: 0.0882 s 231 modules rich_loaded=True (94 of 96 ms is reflex_base.utils.log)

That defeats the lazy_loader design in reflex/init.py. codspeed missed it because there's no import-time benchmark.

I prototyped two fixes and measured both. The cheap one: since reparenting a package root fixes all descendants whenever it happens, bootstrap() doesn't need to run at import reflex at all — move the call into reflex_base/utils/log.py's module body (console.py already imports log at module scope, so it fires the moment reflex does anything) and drop the import from init.py:

self-bootstrapping log.py: 0.0026 s 95 modules (parenting correct once console loads)
import-light bootstrap mod: 0.0265 s 96 modules (~24 ms of that is import logging itself)

  1. Tracebacks get word-wrapped by rich. RichConsoleHandler.emit prints the formatted traceback through console.print, so at 80 cols file paths break mid-path across lines. soft_wrap=True (or crop=False) fixes it.

  2. Deprecation JSON records report the wrong location — .../utils/log.py:740 (the framework frame) rather than the user frame, which is only available inside the message string. Worth overriding for kind="deprecation" since machine consumers are the whole point of that sink.

@FarhanAliRaza

Copy link
Copy Markdown
Contributor Author

@masenf this is fixed.

@FarhanAliRaza
FarhanAliRaza requested a review from masenf August 18, 2026 14:48

@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 5 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/benchmarks/test_import.py">

<violation number="1" location="tests/benchmarks/test_import.py:25">
P3: Importing `reflex` now bootstraps the logging pipeline (`bootstrap()` runs at `reflex_base.utils.log` module scope, mutating loggers' `propagate`/level/handlers, and `import reflex` runs console auto-detection that can print a non-TTY warning / emit records). `_wipe_reflex_modules` + re-import run these side effects once per benchmark iteration, and the `finally` block only restores `sys.modules`; it does not restore the mutated logging/global state. If this test shares a process with other (benchmark or unit) tests, the re-imported reflex leaves the root/reflex loggers and console in a state that no longer matches the restored module objects, contaminating later tests. Include the benchmark scope in isolation, or snapshot/restore logger config as well as `sys.modules`.</violation>
</file>

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

Re-trigger cubic

Comment thread reflex/__init__.py Outdated
Comment thread tests/benchmarks/test_import.py
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/eng-10963-log-1-pipeline branch from 85e40c8 to a92bc1d Compare August 18, 2026 15:47
masenf
masenf previously approved these changes Aug 18, 2026
FarhanAliRaza and others added 7 commits August 18, 2026 22:30
Standard python logging with per-module loggers: rich-rendering console
handler preserving the legacy colors, JSON-lines handler behind
REFLEX_LOG_JSON, record dedupe, and file logging. LogLevel gains a
correct total ordering (the str mixin compared alphabetically) and
to_logging_level(). console.py delegates set_log_level into the
pipeline and respects JSON mode in print/rule/status/progress;
reflex bootstraps the reflex-owned loggers on import.
…arkup

Address review feedback on the logging pipeline:

- Parent every package logger (reflex_base, reflex_cli,
  reflex_components_*) under the top-level "reflex" logger, so all
  reflex logging is tunable in one place (or per package) with standard
  stdlib APIs. Propagation stays enabled in library mode.
- Attach handlers only in managed mode, i.e. under the reflex CLI or a
  worker it spawned (REFLEX_MANAGED_LOGGING marker inherited through the
  environment). A plain "import reflex" no longer touches handlers or
  propagation; records flow to the root logger for the application to
  handle. Managed mode cuts propagation so an app-side basicConfig
  cannot double-emit records or break the --json output contract.
  This also removes the _BootstrapHandler lazy-attach trick.
- Rich markup in log messages is now opt-in via extra={"rich": True};
  plain records keep their literal brackets in every sink instead of
  being markup-stripped.
- The dedupe filter stores key hashes instead of full messages.
- Drop the autouse conftest fixture that re-attached caplog to each
  package root; propagation makes pytest capture work natively.
- share one full-logging file between the pipeline handler and the legacy
  console writer, instead of opening (and unlinking) two files
- create the parent directory of a configured REFLEX_LOG_FILE path
- keep the originating severity in JSON records emitted by the legacy
  console helpers, and route console.log/print_table/PoorProgress through
  JSON mode instead of writing rich text into the machine-readable stream
- keep an application's own level configuration on the package loggers
  when bootstrap reparents them
- drop the duplicate console._LOG_LEVEL state; the log module's level is
  the single source of truth
Eagerly importing reflex_base.utils.log from reflex/__init__.py dragged
in rich and 159 other modules, defeating the lazy_loader design (median
import time 53.7ms vs 1.2ms, 46x). Reparenting is permanent, so
bootstrap() now runs in log.py's own module body — console.py imports it
at module scope, so the hierarchy is fixed the moment reflex does
anything.

Also: print tracebacks with soft_wrap so file paths survive narrow
terminals, and point deprecation JSON records at the user call site
instead of the framework frame. Adds a cold-import CodSpeed benchmark
so the next eager import shows up as a regression.
reflex/__init__.py imports the pipeline on 3.10 on purpose, so the
3.10-deprecation warning renders through the sinks; the import-lightness
assertions only apply on 3.11+.
The dotted side-effect import bound `reflex_base` in reflex/__init__.py
globals on 3.10 only, so `rx.reflex_base` existed on that version alone.
Import the pipeline through a private alias and delete it, as the block
already does for logging.
…branch

The import only bought a "Warning:" prefix for worker subprocesses on a
deprecated Python; the CLI parent has not enabled managed mode at import
time anyway, so the warning goes through logging.lastResort regardless.
reflex/__init__.py now has no knowledge of the pipeline on any version,
and the lazy-import test needs no version branch.
bootstrap() ran configure() in managed mode, and configure() imports
reflex_base.environment, which pulls in the component tree. Since the
pipeline now bootstraps when log.py is first imported, a managed worker
that touched reflex_base.vars before anything else closed a circular
import (vars -> console -> log -> environment -> plugins -> components
-> vars) and died with a partially initialized module.

Import-time bootstrap now only reparents the package loggers, which is
all it ever needed to do at import. Managed processes attach sinks via
enable_managed_logging() (CLI) or ensure_configured() (workers, from
get_config()), both of which run after imports have settled.
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/eng-10963-log-1-pipeline branch from a92bc1d to c8998f0 Compare August 18, 2026 17:36
@Alek99
Alek99 self-requested a review August 18, 2026 19:13
@FarhanAliRaza
FarhanAliRaza merged commit 5194efc into main Aug 18, 2026
116 of 164 checks passed
@FarhanAliRaza
FarhanAliRaza deleted the farhan/eng-10963-log-1-pipeline branch August 18, 2026 22:03
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.

4 participants