11defmodule Sentry.ClientReport do
22 @ moduledoc """
3- A struct and GenServer implementation to represent and manage **client reports** for Sentry .
3+ This module represents the data structure for a **client report** .
44
5- Client reports are used to provide insights into which events are being dropped and for what reason.
5+ Client reports are used to provide insights into which events are being
6+ dropped (that is, not sent to Sentry) and for what reason.
67
78 This module is responsible for recording, storing, and periodically sending these client
89 reports to Sentry. You can choose to turn off these reports by configuring the
9- option ` send_client_reports?` .
10+ `: send_client_reports` option .
1011
1112 Refer to <https://develop.sentry.dev/sdk/client-reports/> for more details.
1213
@@ -15,9 +16,6 @@ defmodule Sentry.ClientReport do
1516
1617 @ moduledoc since: "10.8.0"
1718
18- use GenServer
19- alias Sentry . { Client , Config , Envelope }
20-
2119 @ client_report_reasons [
2220 :ratelimit_backoff ,
2321 :queue_overflow ,
@@ -48,100 +46,10 @@ defmodule Sentry.ClientReport do
4846 discarded_events: [ % { reason: reason ( ) , category: String . t ( ) , quantity: pos_integer ( ) } ]
4947 }
5048
49+ @ enforce_keys [ :timestamp , :discarded_events ]
5150 defstruct [ :timestamp , discarded_events: % { } ]
5251
53- @ send_interval 30_000
54-
55- @ doc false
56- @ spec start_link ( [ ] ) :: GenServer . on_start ( )
57- def start_link ( opts \\ [ ] ) do
58- GenServer . start_link ( __MODULE__ , % { } , name: Keyword . get ( opts , :name , __MODULE__ ) )
59- end
60-
61- @ doc false
62- @ spec record_discarded_events (
63- reason ( ) ,
64- [ item ]
65- ) :: :ok
66- when item:
67- Sentry.Attachment . t ( )
68- | Sentry.CheckIn . t ( )
69- | Sentry.ClientReport . t ( )
70- | Sentry.Event . t ( )
71- def record_discarded_events ( reason , event_items , genserver \\ __MODULE__ )
72- when is_list ( event_items ) do
73- if Enum . member? ( @ client_report_reasons , reason ) do
74- _ =
75- event_items
76- |> Enum . each (
77- & GenServer . cast (
78- genserver ,
79- { :record_discarded_events , reason , Envelope . get_data_category ( & 1 ) }
80- )
81- )
82- end
83-
84- # We silently ignore events whose reasons aren't valid because we have to add it to the allowlist in Snuba
85- # https://develop.sentry.dev/sdk/client-reports/
86-
87- :ok
88- end
89-
90- @ doc false
91- @ impl true
92- def init ( state ) do
93- schedule_report ( )
94- { :ok , state }
95- end
96-
97- @ doc false
98- @ impl true
99- def handle_cast ( { :record_discarded_events , reason , category } , discarded_events ) do
100- { :noreply , Map . update ( discarded_events , { reason , category } , 1 , & ( & 1 + 1 ) ) }
101- end
102-
10352 @ doc false
104- @ impl true
105- def handle_info ( :send_report , discarded_events ) do
106- if map_size ( discarded_events ) != 0 do
107- discarded_events =
108- discarded_events
109- |> Enum . map ( fn { { reason , category } , quantity } ->
110- % {
111- reason: reason ,
112- category: category ,
113- quantity: quantity
114- }
115- end )
116-
117- client_report =
118- % __MODULE__ {
119- timestamp: timestamp ( ) ,
120- discarded_events: discarded_events
121- }
122-
123- _ =
124- if Config . dsn ( ) != nil && Config . send_client_reports? ( ) do
125- Client . send_client_report ( client_report )
126- end
127-
128- schedule_report ( )
129- { :noreply , % { } }
130- else
131- # state is nil so nothing to send but keep looping
132- schedule_report ( )
133- { :noreply , % { } }
134- end
135- end
136-
137- defp schedule_report do
138- Process . send_after ( self ( ) , :send_report , @ send_interval )
139- end
140-
141- defp timestamp do
142- DateTime . utc_now ( )
143- |> DateTime . truncate ( :second )
144- |> DateTime . to_iso8601 ( )
145- |> String . trim_trailing ( "Z" )
146- end
53+ @ spec reasons ( ) :: [ reason ( ) , ... ]
54+ def reasons , do: @ client_report_reasons
14755end
0 commit comments