Litellm add sentry scrubbing (#12210)

* add sentry scrubbing

* add new constants

* remove_unused_import

* sentry scrubbing test

* added unit test
This commit is contained in:
Jugal D. Bhatt 2025-07-02 09:12:36 +05:30 • committed by GitHub
parent 7471a30dcd
commit d322e772f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 0 deletions

View file

@ -803,3 +803,29 @@ SPECIAL_LITELLM_AUTH_TOKEN = ["ui-token"]
DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(
os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)
)
# Sentry Scrubbing Configuration
SENTRY_DENYLIST = [
# API Keys and Tokens
"api_key", "token", "key", "secret", "password", "auth", "credential",
"OPENAI_API_KEY", "ANTHROPIC_API_KEY", "AZURE_API_KEY", "COHERE_API_KEY",
"REPLICATE_API_KEY", "HUGGINGFACE_API_KEY", "TOGETHERAI_API_KEY",
"CLOUDFLARE_API_KEY", "BASETEN_KEY", "OPENROUTER_KEY", "DATAROBOT_API_TOKEN",
"FIREWORKS_API_KEY", "FIREWORKS_AI_API_KEY", "FIREWORKSAI_API_KEY",
# Database and Connection Strings
"database_url", "redis_url", "connection_string",
# Authentication and Security
"master_key", "LITELLM_MASTER_KEY", "auth_token", "jwt_token", "private_key",
"SLACK_WEBHOOK_URL", "webhook_url", "LANGFUSE_SECRET_KEY",
# Email Configuration
"SMTP_PASSWORD", "SMTP_USERNAME", "email_password",
# Cloud Provider Credentials
"aws_access_key", "aws_secret_key", "gcp_credentials",
"azure_credentials", "HCP_VAULT_TOKEN", "CIRCLE_OIDC_TOKEN",
# Proxy and Environment Settings
"proxy_url", "proxy_key", "environment_variables"
]
SENTRY_PII_DENYLIST = [
"user_id", "email", "phone", "address", "ip_address",
"SMTP_SENDER_EMAIL", "TEST_EMAIL_ADDRESS"
]

View file

@ -44,6 +44,8 @@ from litellm.caching.caching_handler import LLMCachingHandler
from litellm.constants import (
DEFAULT_MOCK_RESPONSE_COMPLETION_TOKEN_COUNT,
DEFAULT_MOCK_RESPONSE_PROMPT_TOKEN_COUNT,
SENTRY_DENYLIST,
SENTRY_PII_DENYLIST,
)
from litellm.cost_calculator import (
RealtimeAPITokenUsageProcessor,
@ -2949,6 +2951,10 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
[sys.executable, "-m", "pip", "install", "sentry_sdk"]
)
import sentry_sdk
from sentry_sdk.scrubber import EventScrubber
sentry_sdk_instance = sentry_sdk
sentry_trace_rate = (
os.environ.get("SENTRY_API_TRACE_RATE")
@ -2966,6 +2972,11 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
sample_rate=float(
sentry_sample_rate if sentry_sample_rate else 1.0
),
send_default_pii=False, # Prevent sending Personal Identifiable Information
event_scrubber=EventScrubber(
denylist=SENTRY_DENYLIST,
pii_denylist=SENTRY_PII_DENYLIST
),
)
capture_exception = sentry_sdk_instance.capture_exception
add_breadcrumb = sentry_sdk_instance.add_breadcrumb

View file

@ -13,6 +13,7 @@ import time
from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging
from litellm.litellm_core_utils.litellm_logging import set_callbacks
from litellm.constants import SENTRY_DENYLIST, SENTRY_PII_DENYLIST
@pytest.fixture
@ -309,3 +310,37 @@ def test_response_cost_calculator_with_response_cost_in_hidden_params(logging_ob
assert response_cost is not None
assert response_cost > 100
def test_sentry_event_scrubber_initialization(monkeypatch):
# Step 1: Create a fake sentry_sdk.scrubber module
mock_event_scrubber_instance = MagicMock()
mock_event_scrubber_cls = MagicMock(return_value=mock_event_scrubber_instance)
mock_scrubber_module = MagicMock()
mock_scrubber_module.EventScrubber = mock_event_scrubber_cls
# Step 2: Create a fake sentry_sdk module and insert into sys.modules
mock_sentry_sdk = MagicMock()
mock_sentry_sdk.scrubber = mock_scrubber_module
mock_init = MagicMock()
mock_sentry_sdk.init = mock_init
# Step 3: Inject both into sys.modules BEFORE import occurs
sys.modules["sentry_sdk"] = mock_sentry_sdk
sys.modules["sentry_sdk.scrubber"] = mock_scrubber_module
# Step 4: Run the actual sentry setup code
set_callbacks(["sentry"])
# Step 5: Assert the EventScrubber was constructed correctly
mock_event_scrubber_cls.assert_called_once_with(
denylist=SENTRY_DENYLIST,
pii_denylist=SENTRY_PII_DENYLIST,
)
# Step 6: Assert the event_scrubber and PII args were passed
mock_init.assert_called_once()
call_args = mock_init.call_args[1]
assert call_args["event_scrubber"] == mock_event_scrubber_instance
assert call_args["send_default_pii"] is False