Packages
sentry
13.3.0
13.3.0
13.2.0
13.1.0
13.0.1
13.0.0
12.0.3
12.0.2
12.0.1
12.0.0
11.0.4
11.0.3
11.0.2
11.0.1
11.0.0
10.10.0
10.9.0
10.8.1
10.8.0
10.7.1
10.7.0
10.6.2
10.6.1
10.6.0
10.5.0
10.4.0
10.3.0
10.2.1
10.2.0
10.2.0-rc.2
10.2.0-rc.1
10.1.0
10.0.3
10.0.2
10.0.1
10.0.0
9.1.0
9.0.0
8.1.0
8.0.6
8.0.5
8.0.4
8.0.3
8.0.2
8.0.1
8.0.0
8.0.0-rc.2
8.0.0-rc.1
8.0.0-rc.0
retired
7.2.5
7.2.4
7.2.3
7.2.2
7.2.1
7.2.0
7.1.0
7.0.6
7.0.5
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.4.2
6.4.1
6.4.0
6.3.0
6.2.1
6.2.0
6.1.0
6.0.5
6.0.4
6.0.3
6.0.2
6.0.1
6.0.0
5.0.1
5.0.0
4.0.3
4.0.2
4.0.1
4.0.0
3.0.0
2.2.0
2.1.0
2.0.2
2.0.1
2.0.0
1.1.2
1.1.1
1.1.0
1.0.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
The Official Elixir client for Sentry
Current section
Files
Jump to
Current section
Files
lib/sentry/logger_handler/rate_limiter.ex
defmodule Sentry.LoggerHandler.RateLimiter do
@moduledoc false
# A generic rate-limiter implemented on top of :atomics (requires OTP 21.2+).
# Allows you to rate limit to N events every M milliseconds.
use GenServer
@type id() :: :logger.handler_id()
@atomics_index 1
## Public API
@spec start_under_sentry_supervisor(id(), keyword()) :: Supervisor.on_start_child()
def start_under_sentry_supervisor(id, options) when is_atom(id) and is_list(options) do
max_events = Keyword.fetch!(options, :max_events)
interval = Keyword.fetch!(options, :interval)
spec = Supervisor.child_spec(child_spec({id, max_events, interval}), id: {__MODULE__, id})
case Supervisor.start_child(Sentry.Supervisor, spec) do
{:ok, _pid} -> :ok
{:error, {:already_started, _pid}} -> :ok
{:error, reason} -> {:error, reason}
end
end
@spec terminate_and_delete(id()) :: :ok
def terminate_and_delete(id) when is_atom(id) do
_ = Supervisor.terminate_child(Sentry.Supervisor, child_id(id))
_ = Supervisor.delete_child(Sentry.Supervisor, child_id(id))
:ok
end
@spec start_link({id(), non_neg_integer(), non_neg_integer()}) :: GenServer.on_start()
def start_link({id, max_events, interval}) do
GenServer.start_link(__MODULE__, {id, max_events, interval})
end
@spec increment(id()) :: :ok | :rate_limited
def increment(id) when is_atom(id) do
{atomics_ref, max_events} = :persistent_term.get(persistent_term_key(id))
value = :atomics.add_get(atomics_ref, @atomics_index, _incr = 1)
if value > max_events do
:rate_limited
else
:ok
end
end
## Callbacks
@impl GenServer
def init({id, max_events, interval}) do
atomics_ref = :atomics.new(_size = 1, signed: false)
:persistent_term.put(persistent_term_key(id), {atomics_ref, max_events})
_ = :timer.send_interval(interval, :tick)
{:ok, id}
end
@impl GenServer
def handle_info(:tick, id) do
{atomics_ref, _max_events} = :persistent_term.get(persistent_term_key(id))
:ok = :atomics.put(atomics_ref, @atomics_index, 0)
{:noreply, id}
end
@compile {:inline, persistent_term_key: 1}
defp persistent_term_key(id), do: {__MODULE__, id}
defp child_id(id), do: {__MODULE__, id}
end