88This is not tested in this file.
99"""
1010
11+ import random
1112from unittest import mock
1213
1314import pytest
@@ -117,7 +118,7 @@ def test_dsc_continuation_of_trace(sentry_init, capture_envelopes):
117118
118119 assert "sample_rate" in envelope_trace_header
119120 assert type (envelope_trace_header ["sample_rate" ]) == str
120- assert envelope_trace_header ["sample_rate" ] == "1.0 "
121+ assert envelope_trace_header ["sample_rate" ] == "0.01337 "
121122
122123 assert "sampled" in envelope_trace_header
123124 assert type (envelope_trace_header ["sampled" ]) == str
@@ -137,7 +138,7 @@ def test_dsc_continuation_of_trace(sentry_init, capture_envelopes):
137138
138139
139140def test_dsc_continuation_of_trace_sample_rate_changed_in_traces_sampler (
140- sentry_init , capture_envelopes
141+ sentry_init , capture_envelopes , monkeypatch
141142):
142143 """
143144 Another service calls our service and passes tracing information to us.
@@ -175,10 +176,10 @@ def my_traces_sampler(sampling_context):
175176 }
176177
177178 # We continue the incoming trace and start a new transaction
178- with mock . patch ( "sentry_sdk.tracing_utils.Random.uniform " , return_value = 0.125 ):
179- with sentry_sdk .continue_trace (incoming_http_headers ):
180- with sentry_sdk .start_span (name = "foo" ):
181- pass
179+ monkeypatch . setattr ( random , "random " , lambda : 0.125 )
180+ with sentry_sdk .continue_trace (incoming_http_headers ):
181+ with sentry_sdk .start_span (name = "foo" ):
182+ pass
182183
183184 assert len (envelopes ) == 1
184185
@@ -214,6 +215,212 @@ def my_traces_sampler(sampling_context):
214215 assert envelope_trace_header ["transaction" ] == "bar"
215216
216217
218+ @pytest .mark .parametrize (
219+ "test_data, expected_sample_rate, expected_sampled" ,
220+ [
221+ # Test data:
222+ # "incoming_sample_rate":
223+ # The "sentry-sample_rate" in the incoming `baggage` header.
224+ # "incoming_sampled":
225+ # The "sentry-sampled" in the incoming `baggage` header.
226+ # "sentry_trace_header_parent_sampled":
227+ # The number at the end in the `sentry-trace` header, called "parent_sampled".
228+ # "use_local_traces_sampler":
229+ # Whether the local traces sampler is used.
230+ # "local_traces_sampler_result":
231+ # The result of the local traces sampler.
232+ # "local_traces_sample_rate":
233+ # The `traces_sample_rate` setting in the local `sentry_init` call.
234+ (
235+ {
236+ "incoming_sample_rate" : 1.0 ,
237+ "incoming_sampled" : "true" ,
238+ "sentry_trace_header_parent_sampled" : 1 ,
239+ "use_local_traces_sampler" : False ,
240+ "local_traces_sampler_result" : None ,
241+ "local_traces_sample_rate" : 0.7 ,
242+ },
243+ 1.0 , # expected_sample_rate
244+ "true" , # expected_sampled
245+ ),
246+ (
247+ {
248+ "incoming_sample_rate" : 1.0 ,
249+ "incoming_sampled" : "true" ,
250+ "sentry_trace_header_parent_sampled" : 1 ,
251+ "use_local_traces_sampler" : True ,
252+ "local_traces_sampler_result" : 0.5 ,
253+ "local_traces_sample_rate" : 0.7 ,
254+ },
255+ 0.5 , # expected_sample_rate
256+ "true" , # expected_sampled
257+ ),
258+ (
259+ {
260+ "incoming_sample_rate" : 1.0 ,
261+ "incoming_sampled" : "false" ,
262+ "sentry_trace_header_parent_sampled" : 0 ,
263+ "use_local_traces_sampler" : False ,
264+ "local_traces_sampler_result" : None ,
265+ "local_traces_sample_rate" : 0.7 ,
266+ },
267+ None , # expected_sample_rate
268+ "tracing-disabled-no-transactions-should-be-sent" , # expected_sampled (because the parent sampled is 0)
269+ ),
270+ (
271+ {
272+ "incoming_sample_rate" : 1.0 ,
273+ "incoming_sampled" : "false" ,
274+ "sentry_trace_header_parent_sampled" : 0 ,
275+ "use_local_traces_sampler" : True ,
276+ "local_traces_sampler_result" : 0.5 ,
277+ "local_traces_sample_rate" : 0.7 ,
278+ },
279+ 0.5 , # expected_sample_rate
280+ "false" , # expected_sampled (traces sampler can override parent sampled)
281+ ),
282+ (
283+ {
284+ "incoming_sample_rate" : 1.0 ,
285+ "incoming_sampled" : "true" ,
286+ "sentry_trace_header_parent_sampled" : 1 ,
287+ "use_local_traces_sampler" : False ,
288+ "local_traces_sampler_result" : None ,
289+ "local_traces_sample_rate" : None ,
290+ },
291+ None , # expected_sample_rate
292+ "tracing-disabled-no-transactions-should-be-sent" , # expected_sampled (traces_sample_rate=None disables all transaction creation)
293+ ),
294+ (
295+ {
296+ "incoming_sample_rate" : 1.0 ,
297+ "incoming_sampled" : "true" ,
298+ "sentry_trace_header_parent_sampled" : 1 ,
299+ "use_local_traces_sampler" : True ,
300+ "local_traces_sampler_result" : 0.5 ,
301+ "local_traces_sample_rate" : None ,
302+ },
303+ 0.5 , # expected_sample_rate
304+ "true" , # expected_sampled (traces sampler overrides the traces_sample_rate setting, so transactions are created)
305+ ),
306+ (
307+ {
308+ "incoming_sample_rate" : 1.0 ,
309+ "incoming_sampled" : "false" ,
310+ "sentry_trace_header_parent_sampled" : 0 ,
311+ "use_local_traces_sampler" : False ,
312+ "local_traces_sampler_result" : None ,
313+ "local_traces_sample_rate" : None ,
314+ },
315+ None , # expected_sample_rate
316+ "tracing-disabled-no-transactions-should-be-sent" , # expected_sampled (traces_sample_rate=None disables all transaction creation)
317+ ),
318+ (
319+ {
320+ "incoming_sample_rate" : 1.0 ,
321+ "incoming_sampled" : "false" ,
322+ "sentry_trace_header_parent_sampled" : 0 ,
323+ "use_local_traces_sampler" : True ,
324+ "local_traces_sampler_result" : 0.5 ,
325+ "local_traces_sample_rate" : None ,
326+ },
327+ 0.5 , # expected_sample_rate
328+ "false" , # expected_sampled
329+ ),
330+ (
331+ {
332+ "incoming_sample_rate" : 1.0 ,
333+ "incoming_sampled" : None ,
334+ "sentry_trace_header_parent_sampled" : None ,
335+ "use_local_traces_sampler" : False ,
336+ "local_traces_sampler_result" : 0.5 ,
337+ "local_traces_sample_rate" : 0.7 ,
338+ },
339+ 0.7 , # expected_sample_rate
340+ "true" , # expected_sampled
341+ ),
342+ ],
343+ ids = (
344+ "1 traces_sample_rate does not override incoming" ,
345+ "2 traces_sampler overrides incoming" ,
346+ "3 traces_sample_rate does not overrides incoming sample rate or parent (incoming not sampled)" ,
347+ "4 traces_sampler overrides incoming (incoming not sampled)" ,
348+ "5 forwarding incoming (traces_sample_rate not set)" ,
349+ "6 traces_sampler overrides incoming (traces_sample_rate not set)" ,
350+ "7 forwarding incoming (traces_sample_rate not set) (incoming not sampled)" ,
351+ "8 traces_sampler overrides incoming (traces_sample_rate not set) (incoming not sampled)" ,
352+ "9 traces_sample_rate overrides incoming (upstream deferred sampling decision)" ,
353+ ),
354+ )
355+ def test_dsc_sample_rate_change (
356+ sentry_init ,
357+ capture_envelopes ,
358+ test_data ,
359+ expected_sample_rate ,
360+ expected_sampled ,
361+ ):
362+ """
363+ Another service calls our service and passes tracing information to us.
364+ Our service is continuing the trace, but modifies the sample rate.
365+ The DSC in transaction envelopes should contain the updated sample rate.
366+ """
367+
368+ def my_traces_sampler (sampling_context ):
369+ return test_data ["local_traces_sampler_result" ]
370+
371+ init_kwargs = {
372+ "dsn" :
"https://[email protected] /12312012" ,
373+ 374+ "environment" : "canary" ,
375+ }
376+
377+ if test_data ["local_traces_sample_rate" ]:
378+ init_kwargs ["traces_sample_rate" ] = test_data ["local_traces_sample_rate" ]
379+
380+ if test_data ["use_local_traces_sampler" ]:
381+ init_kwargs ["traces_sampler" ] = my_traces_sampler
382+
383+ sentry_init (** init_kwargs )
384+ envelopes = capture_envelopes ()
385+
386+ # This is what the upstream service sends us
387+ incoming_trace_id = "771a43a4192642f0b136d5159a501700"
388+ if test_data ["sentry_trace_header_parent_sampled" ] is None :
389+ sentry_trace = f"{ incoming_trace_id } -1234567890abcdef"
390+ else :
391+ sentry_trace = f"{ incoming_trace_id } -1234567890abcdef-{ test_data ['sentry_trace_header_parent_sampled' ]} "
392+
393+ baggage = (
394+ f"sentry-trace_id={ incoming_trace_id } , "
395+ f"sentry-sample_rate={ str (test_data ['incoming_sample_rate' ])} , "
396+ f"sentry-sampled={ test_data ['incoming_sampled' ]} , "
397+ "sentry-public_key=frontendpublickey, "
398+ 399+ "sentry-environment=prod, "
400+ "sentry-transaction=foo, "
401+ )
402+ incoming_http_headers = {
403+ "HTTP_SENTRY_TRACE" : sentry_trace ,
404+ "HTTP_BAGGAGE" : baggage ,
405+ }
406+
407+ # We continue the incoming trace and start a new transaction
408+ with mock .patch .object (random , "random" , return_value = 0.2 ):
409+ with sentry_sdk .continue_trace (incoming_http_headers ):
410+ with sentry_sdk .start_span (name = "foo" ):
411+ pass
412+
413+ if expected_sampled == "tracing-disabled-no-transactions-should-be-sent" :
414+ assert len (envelopes ) == 0
415+ else :
416+ transaction_envelope = envelopes [0 ]
417+ dsc_in_envelope_header = transaction_envelope .headers ["trace" ]
418+
419+ assert dsc_in_envelope_header ["sample_rate" ] == str (expected_sample_rate )
420+ assert dsc_in_envelope_header ["sampled" ] == str (expected_sampled ).lower ()
421+ assert dsc_in_envelope_header ["trace_id" ] == incoming_trace_id
422+
423+
217424def test_dsc_issue (sentry_init , capture_envelopes ):
218425 """
219426 Our service is a standalone service that does not have tracing enabled. Just uses Sentry for error reporting.
0 commit comments