fix(logging): leave oversized bodies out of the raw request log

With log_raw_request_response on, every request builds a curl string of the
full body and runs secret redaction over it on the event loop. For a 16 MB
body that blocks the proxy for seconds, so liveness probes time out and the
pod restarts

Bodies longer than MAX_RAW_REQUEST_BODY_LOG_CHARS (default 1,000,000) are
now replaced by a short placeholder in the curl string, which keeps the URL
and masked headers. raw_request_typed_dict still holds the full body

Fixes #49673
This commit is contained in:
Harshil Kaneria 2026-11-03 00:18:03 +05:30
parent 97cd5d0c6a
commit 44862f0dc9
3 changed files with 30 additions and 1 deletions

View file

@ -2184,6 +2184,7 @@ MICROSOFT_USER_LAST_NAME_ATTRIBUTE: Final = str(os.getenv("MICROSOFT_USER_LAST_N
# Maximum payload size (in bytes) to fully serialize for DEBUG logging.
# Payloads larger than this are truncated to avoid multi-second json.dumps blocking the response.
MAX_PAYLOAD_SIZE_FOR_DEBUG_LOG: Final = get_env_int("MAX_PAYLOAD_SIZE_FOR_DEBUG_LOG", 102400) # 100 KB
MAX_RAW_REQUEST_BODY_LOG_CHARS: Final = get_env_int("MAX_RAW_REQUEST_BODY_LOG_CHARS", 1_000_000)
# Policy template enrichment
MAX_COMPETITOR_NAMES: Final = get_env_int("MAX_COMPETITOR_NAMES", 100)

View file

@ -47,6 +47,7 @@ from litellm.constants import (
INTERNAL_CALLBACK_NAMES,
KUBERNETES_POD_ROUTING_KEY,
LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL,
MAX_RAW_REQUEST_BODY_LOG_CHARS,
PROVIDER_REQUEST_ID_HEADERS,
REALTIME_METERED_USAGE_KEY,
REDACTED_BY_LITELLM,
@ -1830,7 +1831,10 @@ class Logging(LiteLLMLoggingBaseClass):
verbose_logger.debug("\x1b[92m%s\x1b[0m\n", curl_command)
def _get_request_body(self, data: dict) -> str:
return str(data)
body: Final = str(data)
if len(body) > MAX_RAW_REQUEST_BODY_LOG_CHARS:
return f"<{len(body)} chars omitted, over MAX_RAW_REQUEST_BODY_LOG_CHARS={MAX_RAW_REQUEST_BODY_LOG_CHARS}>"
return body
def _get_request_curl_command(self, api_base: str, headers: dict | None, additional_args: dict, data: dict) -> str:
masked_api_base: Final = self._get_masked_api_base(api_base)

View file

@ -8662,6 +8662,30 @@ def test_pre_call_redacts_and_masks_raw_request(logging_obj):
assert "key=*****" in raw_api_base
def test_pre_call_omits_raw_request_body_over_size_limit(logging_obj, monkeypatch):
"""Redacting a multi-MB raw request blocks the event loop, so an oversized body is left out of it."""
monkeypatch.setattr("litellm.litellm_core_utils.litellm_logging.MAX_RAW_REQUEST_BODY_LOG_CHARS", 100)
metadata = {"user_api_key_alias": "qa-key"}
logging_obj.model_call_details["litellm_params"] = {"metadata": metadata}
logging_obj.log_raw_request_response = True
body = {"messages": [{"role": "user", "content": "PRIVATE-PHRASE " * 20}]}
logging_obj.pre_call(
input="hi",
api_key="",
additional_args={
"api_base": "https://api.openai.com/v1/chat/completions",
"headers": {},
"complete_input_dict": body,
},
)
raw_request = metadata["raw_request"]
assert "PRIVATE-PHRASE" not in raw_request
assert "https://api.openai.com/v1/chat/completions" in raw_request
assert logging_obj.model_call_details["raw_request_typed_dict"]["raw_request_body"] == body
_PRIVATE_RAW_REQUEST_ARGS: Final = {
"api_base": "https://api.openai.com/v1/chat/completions",
"headers": {},