mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix(logging): honor NO_COLOR / FORCE_COLOR / tty for non-JSON formatter
The plain (non-JSON) formatter hardcoded ANSI color codes, so redirected logs and NO_COLOR-respecting environments got raw escape sequences. Add _should_use_color(): NO_COLOR (no-color.org) wins, FORCE_COLOR overrides, otherwise fall back to isatty() on the handler stream. Applied to the CorrelationPlainFormatter selection. Adds tests for the toggle.
This commit is contained in:
parent
31a67561ab
commit
04eac6abab
3 changed files with 63 additions and 4 deletions
|
|
@ -244,6 +244,17 @@ handler.addFilter(_secret_filter)
|
|||
handler.addFilter(_correlation_filter)
|
||||
|
||||
|
||||
def _should_use_color(stream: object = None) -> bool:
|
||||
# NO_COLOR (https://no-color.org/) wins. FORCE_COLOR overrides. Otherwise tty-detect.
|
||||
if os.getenv("NO_COLOR"):
|
||||
return False
|
||||
if os.getenv("FORCE_COLOR"):
|
||||
return True
|
||||
if stream is None:
|
||||
stream = sys.stderr
|
||||
return bool(getattr(stream, "isatty", lambda: False)())
|
||||
|
||||
|
||||
def _try_parse_json_message(message: str) -> dict[str, Any] | None:
|
||||
"""
|
||||
Try to parse a log message as JSON. Returns parsed dict if valid, else None.
|
||||
|
|
@ -446,10 +457,11 @@ if json_logs:
|
|||
handler.setFormatter(JsonFormatter())
|
||||
_setup_json_exception_handlers(JsonFormatter())
|
||||
else:
|
||||
formatter: Final = CorrelationPlainFormatter(
|
||||
"\033[92m%(asctime)s - %(name)s:%(levelname)s\033[0m: %(filename)s:%(lineno)s - %(message)s",
|
||||
datefmt="%H:%M:%S",
|
||||
)
|
||||
if _should_use_color(handler.stream):
|
||||
fmt = "\033[92m%(asctime)s - %(name)s:%(levelname)s\033[0m: %(filename)s:%(lineno)s - %(message)s"
|
||||
else:
|
||||
fmt = "%(asctime)s - %(name)s:%(levelname)s: %(filename)s:%(lineno)s - %(message)s"
|
||||
formatter: Final = CorrelationPlainFormatter(fmt, datefmt="%H:%M:%S")
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ EXCLUDED_TERMINAL_VARS = {
|
|||
"WT_SESSION",
|
||||
"GNOME_TERMINAL_SCREEN",
|
||||
"ALACRITTY_SOCKET",
|
||||
"NO_COLOR",
|
||||
"FORCE_COLOR",
|
||||
}
|
||||
|
||||
EXCLUDED_KEYS = frozenset(EXCLUDED_TERMINAL_VARS | EXCLUDED_GUARD_ONLY_VARS | EXCLUDED_ROLLOUT_FLAGS)
|
||||
|
|
|
|||
|
|
@ -831,3 +831,48 @@ def test_set_session_id_bounds_length():
|
|||
assert len(session_id_var.get()) == 256
|
||||
finally:
|
||||
session_id_var.reset(token)
|
||||
|
||||
|
||||
# --- color toggle (NO_COLOR / FORCE_COLOR / tty) -----------------------------
|
||||
|
||||
|
||||
class _FakeStream:
|
||||
def __init__(self, is_tty: bool):
|
||||
self._is_tty = is_tty
|
||||
|
||||
def isatty(self) -> bool:
|
||||
return self._is_tty
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clean_color_env(monkeypatch):
|
||||
monkeypatch.delenv("NO_COLOR", raising=False)
|
||||
monkeypatch.delenv("FORCE_COLOR", raising=False)
|
||||
|
||||
|
||||
def test_should_use_color_no_color_env_disables(monkeypatch, clean_color_env):
|
||||
from litellm._logging import _should_use_color
|
||||
|
||||
monkeypatch.setenv("NO_COLOR", "1")
|
||||
# Even on a tty + FORCE_COLOR, NO_COLOR must win.
|
||||
monkeypatch.setenv("FORCE_COLOR", "1")
|
||||
assert _should_use_color(_FakeStream(is_tty=True)) is False
|
||||
|
||||
|
||||
def test_should_use_color_force_color_enables_on_pipe(monkeypatch, clean_color_env):
|
||||
from litellm._logging import _should_use_color
|
||||
|
||||
monkeypatch.setenv("FORCE_COLOR", "1")
|
||||
assert _should_use_color(_FakeStream(is_tty=False)) is True
|
||||
|
||||
|
||||
def test_should_use_color_tty_default_on(clean_color_env):
|
||||
from litellm._logging import _should_use_color
|
||||
|
||||
assert _should_use_color(_FakeStream(is_tty=True)) is True
|
||||
|
||||
|
||||
def test_should_use_color_pipe_default_off(clean_color_env):
|
||||
from litellm._logging import _should_use_color
|
||||
|
||||
assert _should_use_color(_FakeStream(is_tty=False)) is False
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue