fix(logging): keep root logger INFO lines when json_logs is on and LITELLM_LOG is set (#47175)

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Kerry Lu 2026-10-17 14:33:26 -07:00 • committed by GitHub
parent 44fcbc30b0
commit fc69f987dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 0 deletions

View file

@ -1000,6 +1000,8 @@ def _initialize_loggers_with_handler(handler: logging.Handler):
lg.handlers.clear() # remove any existing handlers
lg.addHandler(handler) # add JSON formatter handler
lg.propagate = False # prevent bubbling to parent/root
if (configured_level := os.getenv("LITELLM_LOG")) is not None:
logging.getLogger().setLevel(resolve_log_level(configured_level))
def _get_uvicorn_json_log_config():

View file

@ -89,6 +89,40 @@ def test_json_mode_emits_one_record_per_logger(capfd):
assert "timestamp" in obj, "`timestamp` key missing"
@pytest.mark.parametrize(
("litellm_log", "want_info_lines"),
[("INFO", 2), ("DEBUG", 2), (None, 0)],
)
def test_json_mode_root_logger_honours_litellm_log(monkeypatch, capfd, litellm_log, want_info_lines):
root: Final = logging.getLogger()
saved_level: Final = root.level
saved_handlers: Final = list(root.handlers)
saved_propagate: Final = root.propagate
if litellm_log is None:
monkeypatch.delenv("LITELLM_LOG", raising=False)
else:
monkeypatch.setenv("LITELLM_LOG", litellm_log)
try:
turn_on_json()
logging.getLogger("some.third.party").info("library info line")
logging.info("root info line")
logging.getLogger("some.third.party").warning("library warning line")
out, err = capfd.readouterr()
finally:
root.handlers.clear()
root.handlers.extend(saved_handlers)
root.setLevel(saved_level)
root.propagate = saved_propagate
records = [json.loads(line) for line in (out + err).splitlines() if line.strip()]
info_records = [r for r in records if r["level"] == "INFO"]
assert len(info_records) == want_info_lines, records
if want_info_lines:
assert {r["message"] for r in info_records} == {"library info line", "root info line"}
assert {r["component"] for r in info_records} == {"some.third.party", "root"}
assert [r["message"] for r in records if r["level"] == "WARNING"] == ["library warning line"]
def test_json_formatter_parses_embedded_json_message():
"""
Test that JsonFormatter parses embedded JSON in the message field and promotes