diff --git a/lib/sentry/logger_handler.ex b/lib/sentry/logger_handler.ex index 5e0fe3ee..ea53f907 100644 --- a/lib/sentry/logger_handler.ex +++ b/lib/sentry/logger_handler.ex @@ -87,6 +87,15 @@ defmodule Sentry.LoggerHandler do want to use sync mode, set this option to `0`. This option effectively implements **overload protection**. """ + ], + discard_threshold: [ + type: :non_neg_integer, + default: 500, + doc: """ + *since v10.8.2* - The number of queued events after which this handler will start + to discard events. If you don't want to discard, set this option to `0`. This option + effectively implements **load shedding**. + """ ] ] @@ -231,7 +240,8 @@ defmodule Sentry.LoggerHandler do :tags_from_metadata, :capture_log_messages, :rate_limiting, - :sync_threshold + :sync_threshold, + :discard_threshold ] ## Logger handler callbacks @@ -319,6 +329,10 @@ defmodule Sentry.LoggerHandler do config.rate_limiting && RateLimiter.increment(handler_id) == :rate_limited -> :ok + config.discard_threshold > 0 && + SenderPool.get_queued_events_counter() >= config.discard_threshold -> + :ok + true -> # Logger handlers run in the process that logs, so we already read all the # necessary Sentry context from the process dictionary (when creating the event). diff --git a/test/sentry/logger_handler_test.exs b/test/sentry/logger_handler_test.exs index 86c9bf09..d4310d04 100644 --- a/test/sentry/logger_handler_test.exs +++ b/test/sentry/logger_handler_test.exs @@ -601,14 +601,61 @@ defmodule Sentry.LoggerHandlerTest do end end - defp register_before_send(_context) do + describe "discard threshold" do + @tag handler_config: %{ + discard_threshold: 2, + capture_log_messages: true + }, + send_request: true + + test "discards logged messages", %{sender_ref: ref} do + register_delay() + + Logger.error("First") + assert_receive {^ref, %{message: %{formatted: "First"}}} + + Logger.error("Second") + assert_receive {^ref, %{message: %{formatted: "Second"}}} + + Logger.error("Third") + refute_receive {^ref, _event}, 100 + + Process.sleep(300) + Logger.error("Fourth") + assert_receive {^ref, %{message: %{formatted: "Fourth"}}} + end + end + + defp register_delay do + bypass = Bypass.open() + + put_test_config( + dsn: "http://public:secret@localhost:#{bypass.port}/1", + dedup_events: false, + hackney_opts: [recv_timeout: 500, pool: :sentry_pool] + ) + + Bypass.expect(bypass, fn conn -> + assert conn.request_path == "/api/1/envelope/" + assert conn.method == "POST" + Process.sleep(150) + Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>) + end) + end + + defp register_before_send(context) do pid = self() ref = make_ref() put_test_config( before_send: fn event -> send(pid, {ref, event}) - false + + if Map.get(context, :send_request, false) do + event + else + false + end end, dsn: "http://public:secret@localhost:9392/1" )