mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix(proxy): include litellm_call_id in LLM API exception logs
_log_llm_api_exception() logged the error with only the exception text and a timestamp, with no request-correlating id even though the caller (_handle_llm_api_exception) has self.data in scope with the litellm_call_id. Under concurrent traffic there was no way to tell which client request a timeout/error log line belonged to, so it could not be cross-referenced against the x-litellm-call-id response header or the spend logs table. Thread litellm_call_id into _log_llm_api_exception and emit it via logging extra so the JsonFormatter surfaces it as a first-class field on the structured record. Closes #37532. Signed-off-by: chjnett <cheonhyeonjun583@gmail.com>
This commit is contained in:
parent
cd63c7e5a7
commit
690b2e7d58
2 changed files with 46 additions and 3 deletions
|
|
@ -1418,7 +1418,7 @@ def _has_attribute_error_in_chain(exc: Exception) -> bool:
|
|||
_CLIENT_DISCONNECT_DETAIL: Final = "Client disconnected the request"
|
||||
|
||||
|
||||
def _log_llm_api_exception(e: Exception) -> None:
|
||||
def _log_llm_api_exception(e: Exception, litellm_call_id: str | None = None) -> None:
|
||||
if getattr(e, "status_code", None) == 499 and getattr(e, "detail", None) == _CLIENT_DISCONNECT_DETAIL:
|
||||
verbose_proxy_logger.info(
|
||||
"litellm.proxy.proxy_server._handle_llm_api_exception(): client disconnected, upstream LLM request cancelled"
|
||||
|
|
@ -1429,7 +1429,15 @@ def _log_llm_api_exception(e: Exception) -> None:
|
|||
if is_expected_client_error(e) and not litellm.log_client_error_tracebacks
|
||||
else verbose_proxy_logger.exception
|
||||
)
|
||||
log_fn("litellm.proxy.proxy_server._handle_llm_api_exception(): Exception occured - %s", e)
|
||||
log_fn(
|
||||
"litellm.proxy.proxy_server._handle_llm_api_exception(): Exception occured - %s",
|
||||
e,
|
||||
extra=(
|
||||
{"litellm_call_id": litellm_call_id}
|
||||
if litellm_call_id is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def _cancel_llm_call_on_client_disconnect(
|
||||
|
|
@ -3164,7 +3172,7 @@ class ProxyBaseLLMRequestProcessing:
|
|||
version: str | None = None,
|
||||
):
|
||||
"""Raises ProxyException (OpenAI API compatible) if an exception is raised"""
|
||||
_log_llm_api_exception(e)
|
||||
_log_llm_api_exception(e, self.data.get("litellm_call_id"))
|
||||
# Allow callbacks to transform the error response
|
||||
transformed_exception: Final = await proxy_logging_obj.post_call_failure_hook(
|
||||
user_api_key_dict=user_api_key_dict,
|
||||
|
|
|
|||
|
|
@ -7409,3 +7409,38 @@ def test_log_llm_api_exception_traceback_only_for_unexpected_errors(exc, expect_
|
|||
records = [r for r in caplog.records if "_handle_llm_api_exception(): Exception occured" in r.getMessage()]
|
||||
assert len(records) == 1
|
||||
assert (records[0].exc_info is not None) is expect_traceback
|
||||
|
||||
|
||||
def test_log_llm_api_exception_includes_litellm_call_id(caplog):
|
||||
"""Regression for #37532: proxy error log lines carry litellm_call_id so a
|
||||
logged exception can be correlated back to the request that failed."""
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.common_request_processing import _log_llm_api_exception
|
||||
|
||||
verbose_proxy_logger.propagate = True
|
||||
try:
|
||||
with caplog.at_level("ERROR", logger="LiteLLM Proxy"):
|
||||
_log_llm_api_exception(ValueError("boom"), "abc-123")
|
||||
finally:
|
||||
verbose_proxy_logger.propagate = False
|
||||
|
||||
records = [r for r in caplog.records if "_handle_llm_api_exception(): Exception occured" in r.getMessage()]
|
||||
assert len(records) == 1
|
||||
assert records[0].litellm_call_id == "abc-123"
|
||||
|
||||
|
||||
def test_log_llm_api_exception_without_call_id_omits_field(caplog):
|
||||
"""When no call id is available, the record still logs without the extra field."""
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.common_request_processing import _log_llm_api_exception
|
||||
|
||||
verbose_proxy_logger.propagate = True
|
||||
try:
|
||||
with caplog.at_level("ERROR", logger="LiteLLM Proxy"):
|
||||
_log_llm_api_exception(ValueError("boom"))
|
||||
finally:
|
||||
verbose_proxy_logger.propagate = False
|
||||
|
||||
records = [r for r in caplog.records if "_handle_llm_api_exception(): Exception occured" in r.getMessage()]
|
||||
assert len(records) == 1
|
||||
assert not hasattr(records[0], "litellm_call_id")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue