Skip to content

Commit c0bb9ab

Browse files
committed
Environment variable to force StreamHandler instead of RichHandler
1 parent 37829bf commit c0bb9ab

3 files changed

Lines changed: 59 additions & 13 deletions

File tree

src/constants.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
- Do **NOT** use all uppercase - capitalize only the first letter of significant words
5353
- Exclude articles and prepositions (e.g., "a," "the," "of," "on," "in")
5454
- Exclude all punctuation and interpunction marks (e.g., . , : ; ! ? | "")
55-
- Retain original abbreviations. Do not expand an abbreviation if its specific meaning in the context is unknown or ambiguous.
55+
- Retain original abbreviations. Do not expand an abbreviation if its specific meaning in the
56+
context is unknown or ambiguous.
5657
- Neutral objective language
5758
- Do **NOT** provide explanations, reasoning, or "processing steps".
5859
- Do **NOT** provide multiple options (e.g., do not use "or").
@@ -188,3 +189,6 @@
188189
DEFAULT_LOG_LEVEL = "INFO"
189190
# Default log format for plain-text logging in non-TTY environments
190191
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)-8s %(name)s:%(lineno)d %(message)s"
192+
# Environment variable to force StreamHandler instead of RichHandler
193+
# Set to any non-empty value to disable RichHandler
194+
LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR = "LIGHTSPEED_STACK_DISABLE_RICH_HANDLER"

src/log.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
from rich.logging import RichHandler
88

99
from constants import (
10-
LIGHTSPEED_STACK_LOG_LEVEL_ENV_VAR,
11-
DEFAULT_LOG_LEVEL,
1210
DEFAULT_LOG_FORMAT,
11+
DEFAULT_LOG_LEVEL,
12+
LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR,
13+
LIGHTSPEED_STACK_LOG_LEVEL_ENV_VAR,
1314
)
1415

1516

@@ -38,8 +39,7 @@ def resolve_log_level() -> int:
3839
# through a logger produces inconsistent output depending on root-logger
3940
# state.
4041
print(
41-
f"WARNING: Invalid log level '{level_str}', "
42-
f"falling back to {DEFAULT_LOG_LEVEL}",
42+
f"WARNING: Invalid log level '{level_str}', " f"falling back to {DEFAULT_LOG_LEVEL}",
4343
file=sys.stderr,
4444
)
4545
validated_level = getattr(logging, DEFAULT_LOG_LEVEL)
@@ -49,18 +49,23 @@ def resolve_log_level() -> int:
4949

5050
def create_log_handler() -> logging.Handler:
5151
"""
52-
Create and return a configured log handler based on TTY availability.
52+
Create and return a configured log handler based on TTY availability and environment settings.
5353
54-
If stderr is connected to a terminal (TTY), returns a RichHandler for
55-
rich-formatted console output. Otherwise, returns a StreamHandler with
54+
If LIGHTSPEED_STACK_DISABLE_RICH_HANDLER is set to any non-empty value,
55+
returns a StreamHandler with plain-text formatting. Otherwise, if stderr
56+
is connected to a terminal (TTY), returns a RichHandler for rich-formatted
57+
console output. If neither condition is met, returns a StreamHandler with
5658
plain-text formatting suitable for non-TTY environments (e.g., containers).
5759
58-
Parameters:
59-
None
60-
6160
Returns:
6261
logging.Handler: A configured handler instance (RichHandler or StreamHandler).
6362
"""
63+
# Check if RichHandler is explicitly disabled via environment variable
64+
if os.environ.get(LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR):
65+
handler = logging.StreamHandler()
66+
handler.setFormatter(logging.Formatter(DEFAULT_LOG_FORMAT))
67+
return handler
68+
6469
if sys.stderr.isatty():
6570
# RichHandler's columnar layout assumes a real terminal.
6671
# RichHandler handles its own formatting, so no formatter is set.

tests/unit/test_log.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
from pytest_mock import MockerFixture
77
from rich.logging import RichHandler
88

9-
from log import get_logger, resolve_log_level, create_log_handler
10-
from constants import LIGHTSPEED_STACK_LOG_LEVEL_ENV_VAR, DEFAULT_LOG_FORMAT
9+
from constants import (
10+
DEFAULT_LOG_FORMAT,
11+
LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR,
12+
LIGHTSPEED_STACK_LOG_LEVEL_ENV_VAR,
13+
)
14+
from log import create_log_handler, get_logger, resolve_log_level
1115

1216

1317
def test_get_logger() -> None:
@@ -116,3 +120,36 @@ def test_create_log_handler_non_tty_format(mocker: MockerFixture) -> None:
116120
assert handler.formatter is not None
117121
# pylint: disable=protected-access
118122
assert handler.formatter._fmt == DEFAULT_LOG_FORMAT
123+
124+
125+
def test_create_log_handler_disable_rich_with_tty(
126+
mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch
127+
) -> None:
128+
"""Test that RichHandler is disabled when env var is set, even with TTY."""
129+
mocker.patch("sys.stderr.isatty", return_value=True)
130+
monkeypatch.setenv(LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR, "1")
131+
handler = create_log_handler()
132+
assert isinstance(handler, logging.StreamHandler)
133+
assert not isinstance(handler, RichHandler)
134+
135+
136+
def test_create_log_handler_disable_rich_format(
137+
mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch
138+
) -> None:
139+
"""Test that disabled RichHandler uses DEFAULT_LOG_FORMAT."""
140+
mocker.patch("sys.stderr.isatty", return_value=True)
141+
monkeypatch.setenv(LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR, "true")
142+
handler = create_log_handler()
143+
assert handler.formatter is not None
144+
# pylint: disable=protected-access
145+
assert handler.formatter._fmt == DEFAULT_LOG_FORMAT
146+
147+
148+
def test_create_log_handler_enable_rich_when_env_var_empty(
149+
mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch
150+
) -> None:
151+
"""Test that RichHandler is used when env var is empty string."""
152+
mocker.patch("sys.stderr.isatty", return_value=True)
153+
monkeypatch.setenv(LIGHTSPEED_STACK_DISABLE_RICH_HANDLER_ENV_VAR, "")
154+
handler = create_log_handler()
155+
assert isinstance(handler, RichHandler)

0 commit comments

Comments
 (0)