@@ -24,27 +24,50 @@ if Code.ensure_loaded?(:otel_sampler) do
2424 @ impl true
2525 def should_sample (
2626 ctx ,
27- _trace_id ,
27+ trace_id ,
2828 _links ,
2929 span_name ,
30- _span_kind ,
31- _attributes ,
30+ span_kind ,
31+ attributes ,
3232 config
3333 ) do
3434 result =
3535 if span_name in config [ :drop ] do
3636 { :drop , [ ] , [ ] }
3737 else
38- sample_rate = Sentry.Config . traces_sample_rate ( )
38+ traces_sampler = Sentry.Config . traces_sampler ( )
39+ traces_sample_rate = Sentry.Config . traces_sample_rate ( )
3940
4041 case get_trace_sampling_decision ( ctx ) do
4142 { :inherit , trace_sampled , tracestate } ->
42- decision = if trace_sampled , do: :record_and_sample , else: :drop
43-
44- { decision , [ ] , tracestate }
43+ # If traces_sampler is configured, it can override parent decision
44+ if traces_sampler do
45+ sampling_context =
46+ build_sampling_context (
47+ trace_sampled ,
48+ span_name ,
49+ span_kind ,
50+ attributes ,
51+ trace_id
52+ )
53+
54+ make_sampler_decision ( traces_sampler , sampling_context , tracestate )
55+ else
56+ # No traces_sampler, inherit parent decision
57+ decision = if trace_sampled , do: :record_and_sample , else: :drop
58+ { decision , [ ] , tracestate }
59+ end
4560
4661 :no_trace ->
47- make_sampling_decision ( sample_rate )
62+ # No parent trace, use traces_sampler if available, otherwise traces_sample_rate
63+ if traces_sampler do
64+ sampling_context =
65+ build_sampling_context ( nil , span_name , span_kind , attributes , trace_id )
66+
67+ make_sampler_decision ( traces_sampler , sampling_context , [ ] )
68+ else
69+ make_sampling_decision ( traces_sample_rate )
70+ end
4871 end
4972 end
5073
@@ -121,6 +144,70 @@ if Code.ensure_loaded?(:otel_sampler) do
121144 end
122145 end
123146
147+ defp build_sampling_context ( parent_sampled , span_name , _span_kind , attributes , trace_id ) do
148+ transaction_context = % {
149+ name: span_name ,
150+ op: span_name ,
151+ trace_id: trace_id ,
152+ attributes: attributes
153+ }
154+
155+ % {
156+ parent_sampled: parent_sampled ,
157+ transaction_context: transaction_context
158+ }
159+ end
160+
161+ defp make_sampler_decision ( traces_sampler , sampling_context , _existing_tracestate ) do
162+ try do
163+ result = call_traces_sampler ( traces_sampler , sampling_context )
164+ sample_rate = normalize_sampler_result ( result )
165+
166+ cond do
167+ sample_rate == 0.0 ->
168+ tracestate = build_tracestate ( 0.0 , 1.0 , false )
169+ { :drop , [ ] , tracestate }
170+
171+ sample_rate == 1.0 ->
172+ tracestate = build_tracestate ( 1.0 , 0.0 , true )
173+ { :record_and_sample , [ ] , tracestate }
174+
175+ is_float ( sample_rate ) and sample_rate > 0.0 and sample_rate < 1.0 ->
176+ random_value = :rand . uniform ( )
177+ sampled = random_value < sample_rate
178+ tracestate = build_tracestate ( sample_rate , random_value , sampled )
179+ decision = if sampled , do: :record_and_sample , else: :drop
180+ { decision , [ ] , tracestate }
181+
182+ true ->
183+ # Invalid result, fall back to not sampling
184+ tracestate = build_tracestate ( 0.0 , 1.0 , false )
185+ { :drop , [ ] , tracestate }
186+ end
187+ rescue
188+ error ->
189+ # Log error and fall back to not sampling
190+ require Logger
191+ Logger . warning ( "traces_sampler function failed: #{ inspect ( error ) } " )
192+ tracestate = build_tracestate ( 0.0 , 1.0 , false )
193+ { :drop , [ ] , tracestate }
194+ end
195+ end
196+
197+ defp call_traces_sampler ( fun , sampling_context ) when is_function ( fun , 1 ) do
198+ fun . ( sampling_context )
199+ end
200+
201+ defp call_traces_sampler ( { module , function } , sampling_context ) do
202+ apply ( module , function , [ sampling_context ] )
203+ end
204+
205+ defp normalize_sampler_result ( true ) , do: 1.0
206+ defp normalize_sampler_result ( false ) , do: 0.0
207+ defp normalize_sampler_result ( rate ) when is_float ( rate ) , do: rate
208+ defp normalize_sampler_result ( rate ) when is_integer ( rate ) , do: rate * 1.0
209+ defp normalize_sampler_result ( _ ) , do: 0.0
210+
124211 defp record_discarded_transaction ( ) do
125212 ClientReport.Sender . record_discarded_events ( :sample_rate , "transaction" )
126213 end
0 commit comments