Skip to content

Commit de8cf40

Browse files
Merge pull request #14482 from BerriAI/litellm_dev_09_11_2025_p3
Feature - new `litellm_request_debug=true` flag, enables emitting raw request/response log on single request
2 parents 663dbc6 + c802c47 commit de8cf40

File tree

5 files changed

+128
-45
lines changed

5 files changed

+128
-45
lines changed

docs/my-website/docs/proxy/debugging.md

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,65 +11,103 @@ The proxy also supports json logs. [See here](#json-logs)
1111

1212
**via cli**
1313

14-
```bash
14+
```bash showLineNumbers
1515
$ litellm --debug
1616
```
1717

1818
**via env**
1919

20-
```python
20+
```python showLineNumbers
2121
os.environ["LITELLM_LOG"] = "INFO"
2222
```
2323

2424
## `detailed debug`
2525

2626
**via cli**
2727

28-
```bash
28+
```bash showLineNumbers
2929
$ litellm --detailed_debug
3030
```
3131

3232
**via env**
3333

34-
```python
34+
```python showLineNumbers
3535
os.environ["LITELLM_LOG"] = "DEBUG"
3636
```
3737

3838
### Debug Logs
3939

4040
Run the proxy with `--detailed_debug` to view detailed debug logs
41-
```shell
41+
```shell showLineNumbers
4242
litellm --config /path/to/config.yaml --detailed_debug
4343
```
4444

4545
When making requests you should see the POST request sent by LiteLLM to the LLM on the Terminal output
46-
```shell
46+
```shell showLineNumbers
4747
POST Request Sent from LiteLLM:
4848
curl -X POST \
4949
https://api.openai.com/v1/chat/completions \
5050
-H 'content-type: application/json' -H 'Authorization: Bearer sk-qnWGUIW9****************************************' \
5151
-d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "this is a test request, write a short poem"}]}'
5252
```
5353

54+
## Debug single request
55+
56+
Pass in `litellm_request_debug=True` in the request body
57+
58+
```bash showLineNumbers
59+
curl -L -X POST 'http://0.0.0.0:4000/chat/completions' \
60+
-H 'Content-Type: application/json' \
61+
-H 'Authorization: Bearer sk-1234' \
62+
-d '{
63+
"model":"fake-openai-endpoint",
64+
"messages": [{"role": "user","content": "How many r in the word strawberry?"}],
65+
"litellm_request_debug": true
66+
}'
67+
```
68+
69+
This will emit the raw request sent by LiteLLM to the API Provider and raw response received from the API Provider for **just** this request in the logs.
70+
71+
72+
```bash showLineNumbers
73+
INFO: Uvicorn running on http://0.0.0.0:4000 (Press CTRL+C to quit)
74+
20:14:06 - LiteLLM:WARNING: litellm_logging.py:938 -
75+
76+
POST Request Sent from LiteLLM:
77+
curl -X POST \
78+
https://exampleopenaiendpoint-production.up.railway.app/chat/completions \
79+
-H 'Authorization: Be****ey' -H 'Content-Type: application/json' \
80+
-d '{'model': 'fake', 'messages': [{'role': 'user', 'content': 'How many r in the word strawberry?'}], 'stream': False}'
81+
82+
83+
20:14:06 - LiteLLM:WARNING: litellm_logging.py:1015 - RAW RESPONSE:
84+
{"id":"chatcmpl-817fc08f0d6c451485d571dab39b26a1","object":"chat.completion","created":1677652288,"model":"gpt-3.5-turbo-0301","system_fingerprint":"fp_44709d6fcb","choices":[{"index":0,"message":{"role":"assistant","content":"\n\nHello there, how may I assist you today?"},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":9,"completion_tokens":12,"total_tokens":21}}
85+
86+
87+
INFO: 127.0.0.1:56155 - "POST /chat/completions HTTP/1.1" 200 OK
88+
89+
```
90+
91+
5492
## JSON LOGS
5593

5694
Set `JSON_LOGS="True"` in your env:
5795

58-
```bash
96+
```bash showLineNumbers
5997
export JSON_LOGS="True"
6098
```
6199
**OR**
62100

63101
Set `json_logs: true` in your yaml:
64102

65-
```yaml
103+
```yaml showLineNumbers
66104
litellm_settings:
67105
json_logs: true
68106
```
69107
70108
Start proxy
71109
72-
```bash
110+
```bash showLineNumbers
73111
$ litellm
74112
```
75113

@@ -80,7 +118,7 @@ The proxy will now all logs in json format.
80118
Turn off fastapi's default 'INFO' logs
81119

82120
1. Turn on 'json logs'
83-
```yaml
121+
```yaml showLineNumbers
84122
litellm_settings:
85123
json_logs: true
86124
```
@@ -89,20 +127,20 @@ litellm_settings:
89127

90128
Only get logs if an error occurs.
91129

92-
```bash
130+
```bash showLineNumbers
93131
LITELLM_LOG="ERROR"
94132
```
95133

96134
3. Start proxy
97135

98136

99-
```bash
137+
```bash showLineNumbers
100138
$ litellm
101139
```
102140

103141
Expected Output:
104142

105-
```bash
143+
```bash showLineNumbers
106144
# no info statements
107145
```
108146

@@ -119,14 +157,14 @@ This can be caused due to all your models hitting rate limit errors, causing the
119157
How to control this?
120158
- Adjust the cooldown time
121159
122-
```yaml
160+
```yaml showLineNumbers
123161
router_settings:
124162
cooldown_time: 0 # 👈 KEY CHANGE
125163
```
126164

127165
- Disable Cooldowns [NOT RECOMMENDED]
128166

129-
```yaml
167+
```yaml showLineNumbers
130168
router_settings:
131169
disable_cooldowns: True
132170
```

litellm/litellm_core_utils/get_litellm_params.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def get_litellm_params(
6262
use_litellm_proxy: Optional[bool] = None,
6363
api_version: Optional[str] = None,
6464
max_retries: Optional[int] = None,
65+
litellm_request_debug: Optional[bool] = None,
6566
**kwargs,
6667
) -> dict:
6768
litellm_params = {
@@ -118,5 +119,6 @@ def get_litellm_params(
118119
"vertex_credentials": kwargs.get("vertex_credentials"),
119120
"vertex_project": kwargs.get("vertex_project"),
120121
"use_litellm_proxy": use_litellm_proxy,
122+
"litellm_request_debug": litellm_request_debug,
121123
}
122124
return litellm_params

litellm/litellm_core_utils/litellm_logging.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ class Logging(LiteLLMLoggingBaseClass):
245245
global supabaseClient, promptLayerLogger, weightsBiasesLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app
246246
custom_pricing: bool = False
247247
stream_options = None
248+
litellm_request_debug: bool = False
248249

249250
def __init__(
250251
self,
@@ -470,6 +471,7 @@ def update_environment_variables(
470471
**self.litellm_params,
471472
**scrub_sensitive_keys_in_metadata(litellm_params),
472473
}
474+
self.litellm_request_debug = litellm_params.get("litellm_request_debug", False)
473475
self.logger_fn = litellm_params.get("logger_fn", None)
474476
verbose_logger.debug(f"self.optional_params: {self.optional_params}")
475477

@@ -907,13 +909,19 @@ def _print_llm_call_debugging_log(
907909
908910
Prints the RAW curl command sent from LiteLLM
909911
"""
910-
if _is_debugging_on():
912+
if _is_debugging_on() or self.litellm_request_debug:
911913
if json_logs:
912914
masked_headers = self._get_masked_headers(headers)
913-
verbose_logger.debug(
914-
"POST Request Sent from LiteLLM",
915-
extra={"api_base": {api_base}, **masked_headers},
916-
)
915+
if self.litellm_request_debug:
916+
verbose_logger.warning( # .warning ensures this shows up in all environments
917+
"POST Request Sent from LiteLLM",
918+
extra={"api_base": {api_base}, **masked_headers},
919+
)
920+
else:
921+
verbose_logger.debug(
922+
"POST Request Sent from LiteLLM",
923+
extra={"api_base": {api_base}, **masked_headers},
924+
)
917925
else:
918926
headers = additional_args.get("headers", {})
919927
if headers is None:
@@ -926,7 +934,12 @@ def _print_llm_call_debugging_log(
926934
additional_args=additional_args,
927935
data=data,
928936
)
929-
verbose_logger.debug(f"\033[92m{curl_command}\033[0m\n")
937+
if self.litellm_request_debug:
938+
verbose_logger.warning(
939+
f"\033[92m{curl_command}\033[0m\n"
940+
) # .warning ensures this shows up in all environments
941+
else:
942+
verbose_logger.debug(f"\033[92m{curl_command}\033[0m\n")
930943

931944
def _get_request_body(self, data: dict) -> str:
932945
return str(data)
@@ -983,16 +996,23 @@ def post_call(
983996
self.model_call_details["additional_args"] = additional_args
984997
self.model_call_details["log_event_type"] = "post_api_call"
985998

999+
if self.litellm_request_debug:
1000+
attr = "warning"
1001+
else:
1002+
attr = "debug"
1003+
9861004
if json_logs:
987-
verbose_logger.debug(
1005+
callattr = getattr(verbose_logger, attr)
1006+
callattr(
9881007
"RAW RESPONSE:\n{}\n\n".format(
9891008
self.model_call_details.get(
9901009
"original_response", self.model_call_details
9911010
)
9921011
),
9931012
)
9941013
else:
995-
print_verbose(
1014+
callattr = getattr(verbose_logger, attr)
1015+
callattr(
9961016
"RAW RESPONSE:\n{}\n\n".format(
9971017
self.model_call_details.get(
9981018
"original_response", self.model_call_details
@@ -1714,12 +1734,16 @@ def success_handler( # noqa: PLR0915
17141734
response_obj=result,
17151735
start_time=start_time,
17161736
end_time=end_time,
1717-
litellm_call_id=current_call_id
1718-
if (
1719-
current_call_id := litellm_params.get("litellm_call_id")
1720-
)
1721-
is not None
1722-
else str(uuid.uuid4()),
1737+
litellm_call_id=(
1738+
current_call_id
1739+
if (
1740+
current_call_id := litellm_params.get(
1741+
"litellm_call_id"
1742+
)
1743+
)
1744+
is not None
1745+
else str(uuid.uuid4())
1746+
),
17231747
print_verbose=print_verbose,
17241748
)
17251749
if callback == "wandb" and weightsBiasesLogger is not None:
@@ -3367,6 +3391,7 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
33673391
return galileo_logger # type: ignore
33683392
elif logging_integration == "cloudzero":
33693393
from litellm.integrations.cloudzero.cloudzero import CloudZeroLogger
3394+
33703395
for callback in _in_memory_loggers:
33713396
if isinstance(callback, CloudZeroLogger):
33723397
return callback # type: ignore
@@ -3594,6 +3619,7 @@ def get_custom_logger_compatible_class( # noqa: PLR0915
35943619
return callback
35953620
elif logging_integration == "cloudzero":
35963621
from litellm.integrations.cloudzero.cloudzero import CloudZeroLogger
3622+
35973623
for callback in _in_memory_loggers:
35983624
if isinstance(callback, CloudZeroLogger):
35993625
return callback
@@ -4504,7 +4530,7 @@ def get_standard_logging_object_payload(
45044530

45054531
def emit_standard_logging_payload(payload: StandardLoggingPayload):
45064532
if os.getenv("LITELLM_PRINT_STANDARD_LOGGING_PAYLOAD"):
4507-
print(json.dumps(payload, indent=4)) # noqa
4533+
print(json.dumps(payload, indent=4)) # noqa
45084534

45094535

45104536
def get_standard_logging_metadata(

0 commit comments

Comments
 (0)