diff --git a/lib/sentry/transport/rate_limiter.ex b/lib/sentry/transport/rate_limiter.ex index 4a10ef72..6c01baeb 100644 --- a/lib/sentry/transport/rate_limiter.ex +++ b/lib/sentry/transport/rate_limiter.ex @@ -98,13 +98,14 @@ defmodule Sentry.Transport.RateLimiter do @doc """ Checks whether sending items of the given data category is currently limited. - Logs and metrics have a companion byte category (`log_byte` / - `trace_metric_byte`) that Sentry can limit independently of the count - category, so a limit on either one must suppress sending. Every other category - gates on itself alone. - - So an active `log_byte` limit makes this return `true` for `"log_item"`, even - though `rate_limited?("log_item")` on its own is `false`. + Logs, metrics, and attachments have companion categories (`log_byte`, + `trace_metric_byte`, and `attachment_item`) that Sentry can limit + independently of the count category, so a limit on either one must suppress + sending. Every other category gates on itself alone. + + So an active `log_byte` limit makes this return `true` for `"log_item"`, and + an active `attachment_item` limit does the same for `"attachment"`, even + though the corresponding `rate_limited?/1` call on its own is `false`. """ @spec rate_limited_for_category?(String.t()) :: boolean() def rate_limited_for_category?("log_item"), diff --git a/test/sentry/telemetry_processor_integration_test.exs b/test/sentry/telemetry_processor_integration_test.exs index f6a45fec..a4ae3fd9 100644 --- a/test/sentry/telemetry_processor_integration_test.exs +++ b/test/sentry/telemetry_processor_integration_test.exs @@ -519,6 +519,26 @@ defmodule Sentry.TelemetryProcessorIntegrationTest do } end + test "sends an error without attachments when attachment items are rate limited", ctx do + put_test_config(telemetry_processor_categories: [:error, :log]) + + set_rate_limit("attachment_item", scope: :scheduler) + + :ok = + Sentry.Context.add_attachment(%Sentry.Attachment{filename: "report.txt", data: "report"}) + + on_exit(&Sentry.Context.clear_attachments/0) + + Sentry.capture_message("pre-buffer-attachment-item-limit", result: :none) + + assert [[{%{"type" => "event"}, event}]] = collect_envelopes(ctx.ref, 1, timeout: 2000) + assert event["message"]["formatted"] == "pre-buffer-attachment-item-limit" + + assert collect_discarded_outcomes(ctx.ref, "ratelimit_backoff") == %{ + "attachment" => 1 + } + end + test "sends an error while dropping all of its rate-limited attachments", ctx do put_test_config(telemetry_processor_categories: [:error, :log]) diff --git a/test/sentry/transport/rate_limiter_test.exs b/test/sentry/transport/rate_limiter_test.exs index 1b7fd8e7..194fc6c7 100644 --- a/test/sentry/transport/rate_limiter_test.exs +++ b/test/sentry/transport/rate_limiter_test.exs @@ -87,6 +87,27 @@ defmodule Sentry.Transport.RateLimiterTest do assert RateLimiter.rate_limited?("trace_metric") == false end + test "gates attachments on the attachment limit" do + set_rate_limit("attachment") + + assert RateLimiter.rate_limited_for_category?("attachment") == true + assert RateLimiter.rate_limited?("attachment") == true + end + + test "gates attachments on the attachment_item limit" do + set_rate_limit("attachment_item") + + assert RateLimiter.rate_limited_for_category?("attachment") == true + assert RateLimiter.rate_limited?("attachment") == false + end + + test "does not gate errors on attachment limits" do + set_rate_limit("attachment") + set_rate_limit("attachment_item") + + assert RateLimiter.rate_limited_for_category?("error") == false + end + test "gates a category on itself when it has no companion byte category" do set_rate_limit("error")