@@ -9,6 +9,7 @@ use minitrace::{
99 Span ,
1010} ;
1111use rand:: Rng ;
12+ use regex:: Regex ;
1213
1314use crate :: knobs:: REQUEST_TRACE_SAMPLE_CONFIG ;
1415
@@ -44,22 +45,31 @@ pub fn get_sampled_span<R: Rng>(
4445#[ derive( Debug ) ]
4546pub struct SamplingConfig {
4647 global : f64 ,
47- by_name : BTreeMap < String , f64 > ,
48+ by_name_regex : BTreeMap < String , ( Regex , f64 ) > ,
4849}
4950
5051impl Default for SamplingConfig {
5152 fn default ( ) -> Self {
5253 // No sampling by default
5354 Self {
5455 global : 0.0 ,
55- by_name : BTreeMap :: new ( ) ,
56+ by_name_regex : BTreeMap :: new ( ) ,
5657 }
5758 }
5859}
5960
6061impl SamplingConfig {
6162 fn sample_ratio ( & self , name : & str ) -> f64 {
62- * self . by_name . get ( name) . unwrap_or ( & self . global )
63+ self . by_name_regex
64+ . values ( )
65+ . find_map ( |( name_regex, sample_ratio) | {
66+ if name_regex. is_match ( name) {
67+ Some ( * sample_ratio)
68+ } else {
69+ None
70+ }
71+ } )
72+ . unwrap_or ( self . global )
6373 }
6474}
6575
@@ -68,14 +78,15 @@ impl FromStr for SamplingConfig {
6878
6979 fn from_str ( s : & str ) -> anyhow:: Result < Self > {
7080 let mut global = None ;
71- let mut by_name = BTreeMap :: new ( ) ;
81+ let mut by_name_regex = BTreeMap :: new ( ) ;
7282 for token in s. split ( ',' ) {
7383 let parts: Vec < _ > = token. split ( '=' ) . map ( |s| s. trim ( ) ) . collect ( ) ;
7484 anyhow:: ensure!( parts. len( ) <= 2 , "Too many parts {}" , token) ;
7585 if parts. len ( ) == 2 {
7686 let name = parts[ 0 ] ;
87+ let name_regex = Regex :: new ( name) . context ( "Failed to parse name regex" ) ?;
7788 let rate: f64 = parts[ 1 ] . parse ( ) . context ( "Failed to parse sampling rate" ) ?;
78- let old_value = by_name . insert ( name. to_owned ( ) , rate) ;
89+ let old_value = by_name_regex . insert ( name. to_owned ( ) , ( name_regex , rate) ) ;
7990 anyhow:: ensure!( old_value. is_none( ) , "{} set more than once" , name) ;
8091 } else {
8192 let rate: f64 = parts[ 0 ] . parse ( ) . context ( "Failed to parse sampling rate" ) ?;
@@ -85,7 +96,7 @@ impl FromStr for SamplingConfig {
8596 }
8697 Ok ( SamplingConfig {
8798 global : global. unwrap_or ( 0.0 ) ,
88- by_name ,
99+ by_name_regex ,
89100 } )
90101 }
91102}
@@ -108,24 +119,30 @@ mod tests {
108119 fn test_parse_sampling_config ( ) -> anyhow:: Result < ( ) > {
109120 let config: SamplingConfig = "1" . parse ( ) ?;
110121 assert_eq ! ( config. global, 1.0 ) ;
111- assert_eq ! ( config. by_name . len( ) , 0 ) ;
122+ assert_eq ! ( config. by_name_regex . len( ) , 0 ) ;
112123 assert_eq ! ( config. sample_ratio( "a" ) , 1.0 ) ;
113124
114125 let config: SamplingConfig = "a=0.5,b=0.15" . parse ( ) ?;
115126 assert_eq ! ( config. global, 0.0 ) ;
116- assert_eq ! ( config. by_name . len( ) , 2 ) ;
127+ assert_eq ! ( config. by_name_regex . len( ) , 2 ) ;
117128 assert_eq ! ( config. sample_ratio( "a" ) , 0.5 ) ;
118129 assert_eq ! ( config. sample_ratio( "b" ) , 0.15 ) ;
119130 assert_eq ! ( config. sample_ratio( "c" ) , 0.0 ) ;
120131
121132 let config: SamplingConfig = "a=0.5,b=0.15,0.01" . parse ( ) ?;
122133 assert_eq ! ( config. global, 0.01 ) ;
123- assert_eq ! ( config. by_name . len( ) , 2 ) ;
124- assert_eq ! ( config. by_name . len( ) , 2 ) ;
134+ assert_eq ! ( config. by_name_regex . len( ) , 2 ) ;
135+ assert_eq ! ( config. by_name_regex . len( ) , 2 ) ;
125136 assert_eq ! ( config. sample_ratio( "a" ) , 0.5 ) ;
126137 assert_eq ! ( config. sample_ratio( "b" ) , 0.15 ) ;
127138 assert_eq ! ( config. sample_ratio( "c" ) , 0.01 ) ;
128139
140+ let config: SamplingConfig = "/f/.*=0.5" . parse ( ) ?;
141+ assert_eq ! ( config. by_name_regex. len( ) , 1 ) ;
142+ assert_eq ! ( config. sample_ratio( "/f/a" ) , 0.5 ) ;
143+ assert_eq ! ( config. sample_ratio( "/f/b" ) , 0.5 ) ;
144+ assert_eq ! ( config. sample_ratio( "c" ) , 0.0 ) ;
145+
129146 // Invalid configs.
130147 let err = "100,200" . parse :: < SamplingConfig > ( ) . unwrap_err ( ) ;
131148 assert ! ( format!( "{}" , err) . contains( "Global sampling rate set more than once" ) ) ;
0 commit comments