Fix: log duplication when json_logs is enabled (#19705)

This commit is contained in:
Alexsander Hamir 2026-01-24 11:09:04 -08:00 committed by GitHub
parent 6710abd1fa
commit 9cdd7a8fd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 8 deletions

View File

@ -1,12 +1,40 @@
import json
import logging
import os
from datetime import datetime
class JsonFormatter(logging.Formatter):
def formatTime(self, record, datefmt=None):
dt = datetime.fromtimestamp(record.created)
return dt.isoformat()
def format(self, record):
json_record = {
"message": record.getMessage(),
"level": record.levelname,
"timestamp": self.formatTime(record),
}
if record.exc_info:
json_record["stacktrace"] = self.formatException(record.exc_info)
return json.dumps(json_record)
def _is_json_enabled():
try:
import litellm
return getattr(litellm, 'json_logs', False)
except (ImportError, AttributeError):
return os.getenv("JSON_LOGS", "false").lower() == "true"
# Set up package logger
logger = logging.getLogger("litellm_proxy_extras")
if not logger.handlers: # Only add handler if none exists
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
handler.setFormatter(formatter)
if _is_json_enabled():
handler.setFormatter(JsonFormatter())
else:
handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.INFO)

View File

@ -166,6 +166,66 @@ def _initialize_loggers_with_handler(handler: logging.Handler):
lg.propagate = False # prevent bubbling to parent/root
def _get_uvicorn_json_log_config():
"""
Generate a uvicorn log_config dictionary that applies JSON formatting to all loggers.
This ensures that uvicorn's access logs, error logs, and all application logs
are formatted as JSON when json_logs is enabled.
"""
json_formatter_class = "litellm._logging.JsonFormatter"
# Use the module-level log_level variable for consistency
uvicorn_log_level = log_level.upper()
log_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"()": json_formatter_class,
},
"default": {
"()": json_formatter_class,
},
"access": {
"()": json_formatter_class,
},
},
"handlers": {
"default": {
"formatter": "json",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"uvicorn": {
"handlers": ["default"],
"level": uvicorn_log_level,
"propagate": False,
},
"uvicorn.error": {
"handlers": ["default"],
"level": uvicorn_log_level,
"propagate": False,
},
"uvicorn.access": {
"handlers": ["access"],
"level": uvicorn_log_level,
"propagate": False,
},
},
}
return log_config
def _turn_on_json():
"""
Turn on JSON logging

View File

@ -127,6 +127,7 @@ class ProxyInitializationHelpers:
Get the arguments for `uvicorn` worker
"""
import litellm
from litellm._logging import _get_uvicorn_json_log_config
uvicorn_args = {
"app": "litellm.proxy.proxy_server:app",
@ -137,8 +138,8 @@ class ProxyInitializationHelpers:
print(f"Using log_config: {log_config}") # noqa
uvicorn_args["log_config"] = log_config
elif litellm.json_logs:
print("Using json logs. Setting log_config to None.") # noqa
uvicorn_args["log_config"] = None
# Use JSON log config for uvicorn to ensure all logs (including exceptions) are JSON
uvicorn_args["log_config"] = _get_uvicorn_json_log_config()
if keepalive_timeout is not None:
uvicorn_args["timeout_keep_alive"] = keepalive_timeout
return uvicorn_args