@@ -65,7 +65,7 @@ defmodule Sentry.Test do
6565 Opens a Bypass on a random port, configures the DSN to point to it,
6666 wires up `before_send` / `before_send_log` callbacks to capture structs
6767 in an isolated ETS table, and starts a per-test `Sentry.TelemetryProcessor`
68- (via `setup_telemetry_processor/0 `) so that assertions work for events
68+ (via `setup_telemetry_processor/1 `) so that assertions work for events
6969 that travel through the TelemetryProcessor pipeline (logs, metrics, or
7070 `send_result: :none`).
7171
@@ -79,6 +79,23 @@ defmodule Sentry.Test do
7979 Any extra Sentry config options (e.g., `dedup_events: false`, `traces_sample_rate: 1.0`)
8080 will be forwarded to the test config.
8181
82+ The reserved `:telemetry_processor` option is *not* forwarded to the test
83+ config. Instead, its value (a keyword list) is passed to the per-test
84+ `Sentry.TelemetryProcessor` (e.g. `buffer_configs`, `buffer_capacities`,
85+ `scheduler_weights`, `transport_capacity`). This replaces the need to
86+ manually `stop_supervised!/1` and re-`start_supervised!/2` the processor.
87+
88+ The reserved `:collect_envelopes` option is *not* forwarded to the test
89+ config either. When set, a Bypass envelope collector is wired up
90+ automatically and its reference is returned under the `:ref` key:
91+
92+ * `true` — set up the collector with no options;
93+ * a keyword list — forwarded to `setup_bypass_envelope_collector/2`
94+ (e.g. `[type: "check_in"]` to only collect a given item type).
95+
96+ This collapses the common `bypass = setup_sentry(...); ref =
97+ setup_bypass_envelope_collector(bypass)` two-step into one call.
98+
8299 ## Examples
83100
84101 setup do
@@ -89,28 +106,38 @@ defmodule Sentry.Test do
89106 Sentry.Test.setup_sentry(dedup_events: false)
90107 end
91108
92- Replacing the auto-started processor with a custom-configured one :
109+ Configuring the per-test processor (e.g. a smaller log batch size) :
93110
94111 setup do
95- %{telemetry_processor: name} = ctx = Sentry.Test.setup_sentry()
96- stop_supervised!(name)
97-
98- start_supervised!(
99- {Sentry.TelemetryProcessor,
100- name: name, buffer_configs: %{log: %{batch_size: 1}}},
101- id: name
112+ Sentry.Test.setup_sentry(
113+ telemetry_processor: [buffer_configs: %{log: %{batch_size: 1}}]
102114 )
115+ end
103116
104- ctx
117+ Collecting envelopes directly as the ExUnit setup return:
118+
119+ setup do
120+ Sentry.Test.setup_sentry(collect_envelopes: true, traces_sample_rate: 1.0)
121+ end
122+
123+ test "...", %{ref: ref} do
124+ # ...
105125 end
106126
107127 """
108128 @ doc since: "13.0.0"
109- @ spec setup_sentry ( keyword ( ) ) :: % { bypass: term ( ) , telemetry_processor: atom ( ) }
129+ @ spec setup_sentry ( keyword ( ) ) :: % {
130+ :bypass => term ( ) ,
131+ :telemetry_processor => atom ( ) ,
132+ optional ( :ref ) => reference ( )
133+ }
110134 def setup_sentry ( extra_config \\ [ ] ) do
111135 ensure_bypass_loaded! ( )
112136 ensure_nimble_ownership_loaded! ( )
113137
138+ { tp_opts , extra_config } = Keyword . pop ( extra_config , :telemetry_processor , [ ] )
139+ { collect_envelopes , extra_config } = Keyword . pop ( extra_config , :collect_envelopes , false )
140+
114141 # Open a per-test Bypass and stub the envelope endpoint
115142 bypass = Bypass . open ( )
116143
@@ -120,13 +147,25 @@ defmodule Sentry.Test do
120147
121148 # Start a per-test TelemetryProcessor before setup_collector/1 so that
122149 # the collector wires this test's scheduler into its registry.
123- processor_name = setup_telemetry_processor ( )
150+ processor_name = setup_telemetry_processor ( tp_opts )
124151
125152 # Set up collector with DSN pointing to this test's Bypass
126153 bypass_config = [ dsn: "http://public:secret@localhost:#{ bypass . port } /1" ]
127154 setup_collector ( bypass_config ++ extra_config )
128155
129- % { bypass: bypass , telemetry_processor: processor_name }
156+ case collect_envelopes do
157+ false ->
158+ % { bypass: bypass , telemetry_processor: processor_name }
159+
160+ collect ->
161+ collector_opts = if is_list ( collect ) , do: collect , else: [ ]
162+
163+ % {
164+ bypass: bypass ,
165+ telemetry_processor: processor_name ,
166+ ref: setup_bypass_envelope_collector ( bypass , collector_opts )
167+ }
168+ end
130169 end
131170
132171 @ doc """
@@ -153,34 +192,69 @@ defmodule Sentry.Test do
153192 Must be called from within an ExUnit test because it uses
154193 `ExUnit.Callbacks.start_supervised!/2` for automatic cleanup.
155194
156- If a per-test processor is already registered for this test (for example
157- when using `Sentry.Case`), this function is idempotent and returns the
158- existing processor name instead of starting a new one.
195+ ## Options
196+
197+ `tp_opts` is a keyword list forwarded to the per-test
198+ `Sentry.TelemetryProcessor` child spec (e.g. `buffer_configs`,
199+ `buffer_capacities`, `scheduler_weights`, `transport_capacity`).
200+
201+ Idempotency depends on `tp_opts`:
202+
203+ * with no `tp_opts`, an already-registered live processor (for example
204+ one started by `Sentry.Case`) is reused and its name returned;
205+ * with `tp_opts`, an already-registered live processor is stopped and
206+ restarted under the same name with the given options, so callers no
207+ longer need to `stop_supervised!/1` + `start_supervised!/2` manually.
159208 """
160209 @ doc since: "13.0.0"
161- @ spec setup_telemetry_processor ( ) :: atom ( )
162- def setup_telemetry_processor do
210+ @ spec setup_telemetry_processor ( keyword ( ) ) :: atom ( )
211+ def setup_telemetry_processor ( tp_opts \\ [ ] ) do
163212 case Process . get ( :sentry_telemetry_processor ) do
164213 name when is_atom ( name ) and not is_nil ( name ) ->
165- if processor_alive? ( name ) , do: name , else: start_telemetry_processor ( )
214+ cond do
215+ not processor_alive? ( name ) -> start_telemetry_processor ( tp_opts )
216+ tp_opts == [ ] -> name
217+ true -> restart_telemetry_processor ( name , tp_opts )
218+ end
166219
167220 _ ->
168- start_telemetry_processor ( )
221+ start_telemetry_processor ( tp_opts )
169222 end
170223 end
171224
172- defp start_telemetry_processor do
225+ defp start_telemetry_processor ( tp_opts ) do
173226 uid = System . unique_integer ( [ :positive ] )
174227 processor_name = :"test_telemetry_processor_#{ uid } "
175228
176- ExUnit.Callbacks . start_supervised! (
177- { Sentry.TelemetryProcessor ,
178- name: processor_name , processor_resolver: & Sentry.Test.Registry . lookup_processor_for / 1 } ,
179- id: processor_name
180- )
229+ start_processor_child ( processor_name , tp_opts )
181230
231+ # Must be set before tag_scheduler/1, which reads
232+ # `:sentry_telemetry_processor` from this process's dictionary via
233+ # `fetch_owner_processor/1`. Tagging would otherwise be a silent no-op.
182234 Process . put ( :sentry_telemetry_processor , processor_name )
183235
236+ tag_scheduler ( processor_name )
237+ processor_name
238+ end
239+
240+ defp restart_telemetry_processor ( name , tp_opts ) do
241+ ExUnit.Callbacks . stop_supervised! ( name )
242+ start_processor_child ( name , tp_opts )
243+ # The process dictionary already holds `name`; the new scheduler pid
244+ # must be re-tagged since the old one died with the old supervisor.
245+ tag_scheduler ( name )
246+ name
247+ end
248+
249+ defp start_processor_child ( name , tp_opts ) do
250+ opts =
251+ [ name: name , processor_resolver: & Sentry.Test.Registry . lookup_processor_for / 1 ]
252+ |> Keyword . merge ( tp_opts )
253+
254+ ExUnit.Callbacks . start_supervised! ( { Sentry.TelemetryProcessor , opts } , id: name )
255+ end
256+
257+ defp tag_scheduler ( processor_name ) do
184258 scheduler_pid = Sentry.TelemetryProcessor . get_scheduler ( processor_name )
185259
186260 if scheduler_pid do
@@ -191,7 +265,7 @@ defmodule Sentry.Test do
191265 tag_processor_for_allowed_pid ( self ( ) , scheduler_pid )
192266 end
193267
194- processor_name
268+ :ok
195269 end
196270
197271 defp processor_alive? ( name ) do
@@ -366,9 +440,9 @@ defmodule Sentry.Test do
366440 # callback drops the event.
367441 #
368442 # The owner's processor name is looked up from its process
369- # dictionary; tests set it in `setup_telemetry_processor/0 `. If the
443+ # dictionary; tests set it in `setup_telemetry_processor/1 `. If the
370444 # owner has no per-test processor (e.g. legacy
371- # `start_collecting/1` without `setup_telemetry_processor/0 `), the
445+ # `start_collecting/1` without `setup_telemetry_processor/1 `), the
372446 # tag is skipped and the buffered event still falls back to the
373447 # global processor — the same behaviour as before this change.
374448 defp tag_processor_for_allowed_pid ( owner_pid , allowed_pid ) do
0 commit comments