From 68f96276d8f5f7ce9022704c335c17b4b6bce0d5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Jan 2026 05:38:39 +0000 Subject: [PATCH] Fix json_logs issue: Replace print() statements with logger calls This fix addresses the issue where some startup logs were printed as plain text with ANSI color codes instead of being formatted as JSON when json_logs: true is enabled. Root cause: - print() statements in proxy_cli.py and proxy_server.py bypassed the configured JsonFormatter - ANSI color codes were hardcoded in the messages Changes: 1. litellm/proxy/proxy_cli.py: - Replaced 2 print() calls with verbose_proxy_logger.info() 2. litellm/proxy/proxy_server.py: - Replaced print() calls for success/failure callbacks - Replaced print() calls for model list initialization - Removed ANSI color codes from all messages - Consolidated model list logging into single line Impact: - All startup logs now respect json_logs configuration - No more ANSI color codes in logs - Logs are now parseable by JSON aggregators - ERROR logs with exceptions already worked (unchanged) Testing: Run ./reproduce_json_logs.sh to verify all logs are JSON formatted. --- FIX_DOCUMENTATION.md | 61 +++++++++++++++++++++++++++++++++++ litellm/proxy/proxy_cli.py | 6 ++-- litellm/proxy/proxy_server.py | 14 +++----- 3 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 FIX_DOCUMENTATION.md diff --git a/FIX_DOCUMENTATION.md b/FIX_DOCUMENTATION.md new file mode 100644 index 00000000000..7141d56d3ce --- /dev/null +++ b/FIX_DOCUMENTATION.md @@ -0,0 +1,61 @@ +# Fix for json_logs Issue + +## Problem +When `json_logs: true` is enabled in litellm proxy configuration, some logs were printed as plain text with ANSI color codes instead of being formatted as JSON. + +## Root Cause +Several `print()` statements in `proxy_cli.py` and `proxy_server.py` were outputting logs directly to stdout instead of using the configured logger. These bypassed the JsonFormatter entirely. + +## Solution +Replaced `print()` statements with `verbose_proxy_logger.info()` calls in the following locations: + +### Files Changed: +1. **litellm/proxy/proxy_cli.py** + - Line 137: `print(f"Using log_config: {log_config}")` → `verbose_proxy_logger.info(f"Using log_config: {log_config}")` + - Line 140: `print("Using json logs. Setting log_config to None.")` → `verbose_proxy_logger.info("Using json logs. Setting log_config to None.")` + +2. **litellm/proxy/proxy_server.py** + - Lines 2425-2427: Replaced print statement for success callbacks + - Lines 2443-2445: Replaced print statement for failure callbacks + - Lines 2675-2679: Replaced print statements for model list initialization + - Removed ANSI color codes (`\033[94m`, `\033[32m`, etc.) from all messages + +## Changes in Detail + +### Before: +```python +print(f"{blue_color_code} Initialized Success Callbacks - {litellm.success_callback} {reset_color_code}") +``` + +### After: +```python +verbose_proxy_logger.info(f"Initialized Success Callbacks - {litellm.success_callback}") +``` + +### Model List Logging: +**Before:** Printed header + one line per model +```python +print("\033[32mLiteLLM: Proxy initialized with Config, Set models:\033[0m") +for model in model_list: + print(f"\033[32m {model.get('model_name', '')}\033[0m") +``` + +**After:** Single log line with all models +```python +model_names = [model.get('model_name', '') for model in model_list] +verbose_proxy_logger.info(f"LiteLLM: Proxy initialized with Config, Set models: {', '.join(model_names)}") +``` + +## Impact +- ✅ All startup INFO logs now use the configured JsonFormatter when `json_logs: true` +- ✅ No more ANSI color codes in logs +- ✅ Logs are now parseable by JSON log aggregators (Elasticsearch, Splunk, etc.) +- ✅ ERROR logs with exceptions already worked correctly (unchanged) + +## Testing +Run the reproducer script to verify: +```bash +./reproduce_json_logs.sh +``` + +Expected: All logs after "Using json logs" should be in JSON format with no ANSI color codes. diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index 2059246674b..85c319d1471 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -128,16 +128,18 @@ class ProxyInitializationHelpers: """ import litellm + from litellm._logging import verbose_proxy_logger + uvicorn_args = { "app": "litellm.proxy.proxy_server:app", "host": host, "port": port, } if log_config is not None: - print(f"Using log_config: {log_config}") # noqa + verbose_proxy_logger.info(f"Using log_config: {log_config}") uvicorn_args["log_config"] = log_config elif litellm.json_logs: - print("Using json logs. Setting log_config to None.") # noqa + verbose_proxy_logger.info("Using json logs. Setting log_config to None.") uvicorn_args["log_config"] = None if keepalive_timeout is not None: uvicorn_args["timeout_keep_alive"] = keepalive_timeout diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c3a4de314e5..64e75c979b6 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2422,9 +2422,7 @@ class ProxyConfig: "mounting metrics endpoint" ) PrometheusLogger._mount_metrics_endpoint() - print( # noqa - f"{blue_color_code} Initialized Success Callbacks - {litellm.success_callback} {reset_color_code}" - ) # noqa + verbose_proxy_logger.info(f"Initialized Success Callbacks - {litellm.success_callback}") elif key == "failure_callback": litellm.failure_callback = [] @@ -2440,9 +2438,7 @@ class ProxyConfig: litellm.logging_callback_manager.add_litellm_failure_callback( callback ) - print( # noqa - f"{blue_color_code} Initialized Failure Callbacks - {litellm.failure_callback} {reset_color_code}" - ) # noqa + verbose_proxy_logger.info(f"Initialized Failure Callbacks - {litellm.failure_callback}") elif key == "cache_params": # this is set in the cache branch # see usage here: https://docs.litellm.ai/docs/proxy/caching @@ -2672,15 +2668,13 @@ class ProxyConfig: model_list = config.get("model_list", None) if model_list: router_params["model_list"] = model_list - print( # noqa - "\033[32mLiteLLM: Proxy initialized with Config, Set models:\033[0m" - ) # noqa + model_names = [model.get('model_name', '') for model in model_list] + verbose_proxy_logger.info(f"LiteLLM: Proxy initialized with Config, Set models: {', '.join(model_names)}") for model in model_list: ### LOAD FROM os.environ/ ### for k, v in model["litellm_params"].items(): if isinstance(v, str) and v.startswith("os.environ/"): model["litellm_params"][k] = get_secret(v) - print(f"\033[32m {model.get('model_name', '')}\033[0m") # noqa litellm_model_name = model["litellm_params"]["model"] litellm_model_api_base = model["litellm_params"].get("api_base", None) if "ollama" in litellm_model_name and litellm_model_api_base is None: