Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/6863.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Framework logging now flows through standard python `logging` with per-module loggers (`reflex_base.utils.log`, re-exported as `reflex.utils.log`), bootstrapped on `import reflex`. Rich colored output is preserved at the sink, and `REFLEX_LOG_JSON` emits machine-readable JSON-lines records. `--loglevel critical` no longer prints the system-info banner (broken `LogLevel` string-compare ordering fixed).
1 change: 1 addition & 0 deletions packages/reflex-base/news/6863.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `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` gained a correct total ordering and `to_logging_level()`, and the interactive console helpers (`print`/`rule`/`status`/`progress`) now respect JSON mode.
60 changes: 58 additions & 2 deletions packages/reflex-base/src/reflex_base/constants/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import logging
import platform
from enum import Enum
from importlib import metadata
Expand Down Expand Up @@ -248,6 +249,19 @@ def from_string(cls, level: str | None) -> LogLevel | None:
except KeyError:
return None

# The str mixin supplies alphabetical comparisons, so all four operators
# must be overridden to compare by verbosity rank instead.
def __lt__(self, other: LogLevel) -> bool:
"""Compare log levels.

Args:
other: The other log level.

Returns:
True if the log level is less verbose than the other log level.
"""
return _LOG_LEVEL_RANK[self] < _LOG_LEVEL_RANK[other]

def __le__(self, other: LogLevel) -> bool:
"""Compare log levels.

Expand All @@ -257,8 +271,39 @@ def __le__(self, other: LogLevel) -> bool:
Returns:
True if the log level is less than or equal to the other log level.
"""
levels = list(LogLevel)
return levels.index(self) <= levels.index(other)
return _LOG_LEVEL_RANK[self] <= _LOG_LEVEL_RANK[other]

def __gt__(self, other: LogLevel) -> bool:
"""Compare log levels.

Args:
other: The other log level.

Returns:
True if the log level is more verbose-restrictive than the other.
"""
return _LOG_LEVEL_RANK[self] > _LOG_LEVEL_RANK[other]

def __ge__(self, other: LogLevel) -> bool:
"""Compare log levels.

Args:
other: The other log level.

Returns:
True if the log level is greater than or equal to the other.
"""
return _LOG_LEVEL_RANK[self] >= _LOG_LEVEL_RANK[other]

def to_logging_level(self) -> int:
"""Map this level to a stdlib logging level number.

DEFAULT acts as a threshold equivalent to INFO.

Returns:
The stdlib logging level.
"""
return _LOGGING_LEVELS[self]

def subprocess_level(self):
"""Return the log level for the subprocess.
Expand All @@ -269,6 +314,17 @@ def subprocess_level(self):
return self if self != LogLevel.DEFAULT else LogLevel.WARNING


_LOG_LEVEL_RANK = {level: rank for rank, level in enumerate(LogLevel)}
_LOGGING_LEVELS = {
LogLevel.DEBUG: logging.DEBUG,
LogLevel.DEFAULT: logging.INFO,
LogLevel.INFO: logging.INFO,
LogLevel.WARNING: logging.WARNING,
LogLevel.ERROR: logging.ERROR,
LogLevel.CRITICAL: logging.CRITICAL,
}


# Server socket configuration variables
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000

Expand Down
10 changes: 7 additions & 3 deletions packages/reflex-base/src/reflex_base/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import dataclasses
import enum
import importlib
import logging
import os
from collections.abc import Sequence
from functools import lru_cache
Expand All @@ -27,6 +28,8 @@
from reflex_base.utils.exceptions import EnvironmentVarValueError
from reflex_base.utils.types import GenericType, is_union, value_inside_optional

logger = logging.getLogger(__name__)


def get_default_value_for_field(field: dataclasses.Field) -> Any:
"""Get the default value for a field.
Expand Down Expand Up @@ -705,6 +708,9 @@ class EnvironmentVariables:
# Enable full logging of debug messages to reflex user directory.
REFLEX_ENABLE_FULL_LOGGING: EnvVar[bool] = env_var(False)

# Emit logs as machine-readable JSON records instead of rich console output.
REFLEX_LOG_JSON: EnvVar[bool] = env_var(False)

# Whether to enable hot module replacement
VITE_HMR: EnvVar[bool] = env_var(True)

Expand Down Expand Up @@ -781,13 +787,11 @@ def _load_dotenv_from_files(files: list[Path]):
Args:
files: A list of Path objects representing the environment variable files.
"""
from reflex_base.utils import console

if not files:
return

if load_dotenv is None:
console.error(
logger.error(
Comment thread
FarhanAliRaza marked this conversation as resolved.
"""The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.1.0"`."""
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
return
Expand Down
Loading
Loading