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: