1+ using Sentry . Internal ;
12using Sentry . Internal . Extensions ;
23
34namespace Sentry ;
@@ -24,27 +25,33 @@ private DynamicSamplingContext(
2425 string publicKey ,
2526 bool ? sampled ,
2627 double ? sampleRate = null ,
28+ double ? sampleRand = null ,
2729 string ? release = null ,
2830 string ? environment = null ,
2931 string ? transactionName = null )
3032 {
3133 // Validate and set required values
3234 if ( traceId == SentryId . Empty )
3335 {
34- throw new ArgumentOutOfRangeException ( nameof ( traceId ) ) ;
36+ throw new ArgumentOutOfRangeException ( nameof ( traceId ) , "cannot be empty" ) ;
3537 }
3638
3739 if ( string . IsNullOrWhiteSpace ( publicKey ) )
3840 {
39- throw new ArgumentException ( default , nameof ( publicKey ) ) ;
41+ throw new ArgumentException ( "cannot be empty" , nameof ( publicKey ) ) ;
4042 }
4143
4244 if ( sampleRate is < 0.0 or > 1.0 )
4345 {
44- throw new ArgumentOutOfRangeException ( nameof ( sampleRate ) ) ;
46+ throw new ArgumentOutOfRangeException ( nameof ( sampleRate ) , "Arg invalid if < 0.0 or > 1.0" ) ;
4547 }
4648
47- var items = new Dictionary < string , string > ( capacity : 7 )
49+ if ( sampleRand is < 0.0 or >= 1.0 )
50+ {
51+ throw new ArgumentOutOfRangeException ( nameof ( sampleRand ) , "Arg invalid if < 0.0 or >= 1.0" ) ;
52+ }
53+
54+ var items = new Dictionary < string , string > ( capacity : 8 )
4855 {
4956 [ "trace_id" ] = traceId . ToString ( ) ,
5057 [ "public_key" ] = publicKey ,
@@ -61,6 +68,11 @@ private DynamicSamplingContext(
6168 items . Add ( "sample_rate" , sampleRate . Value . ToString ( CultureInfo . InvariantCulture ) ) ;
6269 }
6370
71+ if ( sampleRand is not null )
72+ {
73+ items . Add ( "sample_rand" , sampleRand . Value . ToString ( "N4" , CultureInfo . InvariantCulture ) ) ;
74+ }
75+
6476 if ( ! string . IsNullOrWhiteSpace ( release ) )
6577 {
6678 items . Add ( "release" , release ) ;
@@ -99,7 +111,7 @@ private DynamicSamplingContext(
99111 return null ;
100112 }
101113
102- if ( items . TryGetValue ( "sampled" , out var sampledString ) && ! bool . TryParse ( sampledString , out _ ) )
114+ if ( items . TryGetValue ( "sampled" , out var sampledString ) && ! bool . TryParse ( sampledString , out var sampled ) )
103115 {
104116 return null ;
105117 }
@@ -111,6 +123,27 @@ private DynamicSamplingContext(
111123 return null ;
112124 }
113125
126+ // See https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value
127+ if ( items . TryGetValue ( "sample_rand" , out var sampleRand ) )
128+ {
129+ if ( ! double . TryParse ( sampleRand , NumberStyles . Float , CultureInfo . InvariantCulture , out var rand ) ||
130+ rand is < 0.0 or >= 1.0 )
131+ {
132+ return null ;
133+ }
134+ }
135+ else
136+ {
137+ var rand = SampleRandHelper . GenerateSampleRand ( traceId ) ;
138+ if ( ! string . IsNullOrEmpty ( sampledString ) )
139+ {
140+ // Ensure sample_rand is consistent with the sampling decision that has already been made
141+ rand = bool . Parse ( sampledString )
142+ ? rand * rate // 0 <= sampleRand < rate
143+ : rate + ( 1 - rate ) * rand ; // rate < sampleRand < 1
144+ }
145+ items . Add ( "sample_rand" , rand . ToString ( "N4" , CultureInfo . InvariantCulture ) ) ;
146+ }
114147 return new DynamicSamplingContext ( items ) ;
115148 }
116149
@@ -121,6 +154,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra
121154 var traceId = transaction . TraceId ;
122155 var sampled = transaction . IsSampled ;
123156 var sampleRate = transaction . SampleRate ! . Value ;
157+ var sampleRand = transaction . SampleRand ;
124158 var transactionName = transaction . NameSource . IsHighQuality ( ) ? transaction . Name : null ;
125159
126160 // These two may not have been set yet on the transaction, but we can get them directly.
@@ -132,6 +166,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra
132166 publicKey ,
133167 sampled ,
134168 sampleRate ,
169+ sampleRand ,
135170 release ,
136171 environment ,
137172 transactionName ) ;
0 commit comments