diff --git a/sentry_sdk/_types.py b/sentry_sdk/_types.py index 8336617a8d..b28c7260ce 100644 --- a/sentry_sdk/_types.py +++ b/sentry_sdk/_types.py @@ -269,7 +269,7 @@ class SDKInfo(TypedDict): "metric_bucket", "monitor", "span", - "log", + "log_item", ] SessionStatus = Literal["ok", "exited", "crashed", "abnormal"] diff --git a/sentry_sdk/envelope.py b/sentry_sdk/envelope.py index 5f7220bf21..7dbbdec5c8 100644 --- a/sentry_sdk/envelope.py +++ b/sentry_sdk/envelope.py @@ -273,7 +273,7 @@ def data_category(self): elif ty == "event": return "error" elif ty == "log": - return "log" + return "log_item" elif ty == "client_report": return "internal" elif ty == "profile": diff --git a/tests/test_envelope.py b/tests/test_envelope.py index d1bc668f05..06f8971dc3 100644 --- a/tests/test_envelope.py +++ b/tests/test_envelope.py @@ -1,4 +1,4 @@ -from sentry_sdk.envelope import Envelope +from sentry_sdk.envelope import Envelope, Item, PayloadRef from sentry_sdk.session import Session from sentry_sdk import capture_event import sentry_sdk.client @@ -239,3 +239,24 @@ def test_envelope_without_headers(): assert len(items) == 1 assert items[0].payload.get_bytes() == b'{"started": "2020-02-07T14:16:00Z"}' + + +def test_envelope_item_data_category_mapping(): + """Test that envelope items map to correct data categories for rate limiting.""" + test_cases = [ + ("event", "error"), + ("transaction", "transaction"), + ("log", "log_item"), + ("session", "session"), + ("attachment", "attachment"), + ("client_report", "internal"), + ("profile", "profile"), + ("profile_chunk", "profile_chunk"), + ("statsd", "metric_bucket"), + ("check_in", "monitor"), + ("unknown_type", "default"), + ] + + for item_type, expected_category in test_cases: + item = Item(payload=PayloadRef(json={"test": "data"}), type=item_type) + assert item.data_category == expected_category diff --git a/tests/test_transport.py b/tests/test_transport.py index c6a1a0a7a7..e493515e9a 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -611,7 +611,7 @@ def test_metric_bucket_limits(capturing_server, response_code, make_client): assert capturing_server.captured[0].path == "/api/132/envelope/" capturing_server.clear_captured() - assert set(client.transport._disabled_until) == set(["metric_bucket"]) + assert set(client.transport._disabled_until) == {"metric_bucket"} client.transport.capture_envelope(envelope) client.capture_event({"type": "transaction"}) @@ -629,6 +629,43 @@ def test_metric_bucket_limits(capturing_server, response_code, make_client): ] +@pytest.mark.parametrize("response_code", [200, 429]) +def test_log_item_limits(capturing_server, response_code, make_client): + client = make_client() + capturing_server.respond_with( + code=response_code, + headers={ + "X-Sentry-Rate-Limits": "4711:log_item:organization:quota_exceeded:custom" + }, + ) + + envelope = Envelope() + envelope.add_item(Item(payload=b"{}", type="log")) + client.transport.capture_envelope(envelope) + client.flush() + + assert len(capturing_server.captured) == 1 + assert capturing_server.captured[0].path == "/api/132/envelope/" + capturing_server.clear_captured() + + assert set(client.transport._disabled_until) == {"log_item"} + + client.transport.capture_envelope(envelope) + client.capture_event({"type": "transaction"}) + client.flush() + + assert len(capturing_server.captured) == 2 + + envelope = capturing_server.captured[0].envelope + assert envelope.items[0].type == "transaction" + envelope = capturing_server.captured[1].envelope + assert envelope.items[0].type == "client_report" + report = parse_json(envelope.items[0].get_bytes()) + assert report["discarded_events"] == [ + {"category": "log_item", "reason": "ratelimit_backoff", "quantity": 1}, + ] + + @pytest.mark.parametrize("response_code", [200, 429]) def test_metric_bucket_limits_with_namespace( capturing_server, response_code, make_client