Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/spend_tracking/spend_log_error_logger.py: 72%

19 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1""" 

2Logging helpers for spend-tracking error paths. 

3 

4Proxy operators have asked for a way to keep both their downstream log sinks 

5and the SpendLogs UI free of the stack traces that the spend-tracking 

6machinery emits when it hits 4xx/5xx or transient DB errors. The errors still 

7need to be logged (and still flow to Sentry via 

8``proxy_logging_obj.failure_handler``), but the multi-line stack traces 

9dominate log volume and clutter the per-row Metadata pane in the UI. 

10 

11The opt-in is a single env var, ``LITELLM_SUPPRESS_SPEND_LOG_TRACEBACKS=true``, 

12gated by ``should_suppress_spend_log_tracebacks``. When it returns ``True``: 

13 * ``spend_log_error`` drops the traceback from the console / structured log 

14 record (this module), and 

15 * the failure callback in ``proxy_track_cost_callback`` drops the 

16 ``error_information.traceback`` field from the SpendLogs row before it is 

17 persisted, so the UI's per-row Metadata pane (which renders the metadata 

18 JSON verbatim) stays clean. The key is omitted entirely rather than set 

19 to ``""`` — ``StandardLoggingPayloadErrorInformation`` marks the field 

20 optional and every downstream consumer uses ``.get("traceback")``. 

21 

22At DEBUG the full traceback is always preserved so operators can still 

23troubleshoot. The UI suppression follows the same gate. 

24""" 

25 

26import logging 

27import os 

28from typing import Any, Final 

29 

30from litellm._logging import verbose_proxy_logger 

31from litellm.secret_managers.main import str_to_bool 

32 

33SUPPRESS_SPEND_LOG_TRACEBACKS_ENV: Final = "LITELLM_SUPPRESS_SPEND_LOG_TRACEBACKS" 

34 

35 

36def _is_suppression_env_enabled() -> bool: 

37 """Read the opt-in env var fresh each call so dynamic flips are honored. 

38 

39 Kept separate from ``should_suppress_spend_log_tracebacks`` so tests and 

40 other call sites can introspect just the env-var state without also 

41 consulting the live logger level. 

42 """ 

43 return str_to_bool(os.getenv(SUPPRESS_SPEND_LOG_TRACEBACKS_ENV)) is True 

44 

45 

46def should_suppress_spend_log_tracebacks() -> bool: 

47 """Return ``True`` when spend-log traceback suppression should apply. 

48 

49 Suppression only kicks in when both: 

50 * the operator opted in via the env var, and 

51 * the proxy logger is at INFO or above (i.e. not DEBUG) — at DEBUG we 

52 still want full tracebacks for troubleshooting. 

53 """ 

54 if not _is_suppression_env_enabled(): 54 ↛ 56line 54 didn't jump to line 56 because the condition on line 54 was always true

55 return False 

56 return not verbose_proxy_logger.isEnabledFor(logging.DEBUG) 

57 

58 

59def spend_log_error( 

60 message: str, 

61 *args: Any, 

62 exc: BaseException | None = None, 

63) -> None: 

64 """Log a spend-tracking error, with the traceback gated on the env var. 

65 

66 By default this behaves like ``verbose_proxy_logger.exception`` — the 

67 active exception (or ``exc`` if supplied) is attached so the formatter 

68 renders its traceback. When ``LITELLM_SUPPRESS_SPEND_LOG_TRACEBACKS`` is 

69 truthy and the logger is at INFO or above, the traceback is dropped and 

70 only ``message % args`` is emitted. 

71 

72 Sentry / ``proxy_logging_obj.failure_handler`` is NOT invoked here — call 

73 sites still own the alerting path. This helper is purely about console / 

74 structured-log output volume. 

75 """ 

76 if should_suppress_spend_log_tracebacks(): 76 ↛ 77line 76 didn't jump to line 77 because the condition on line 76 was never true

77 verbose_proxy_logger.error(message, *args) 

78 return 

79 

80 if exc is not None: 80 ↛ 83line 80 didn't jump to line 83 because the condition on line 80 was always true

81 verbose_proxy_logger.error(message, *args, exc_info=(type(exc), exc, exc.__traceback__)) 

82 else: 

83 verbose_proxy_logger.error(message, *args, exc_info=True)