@@ -39,6 +39,7 @@ pub(super) struct BorrowedAggregationKey<'a> {
3939 http_endpoint : & ' a str ,
4040 grpc_status_code : Option < u8 > ,
4141 service_source : & ' a str ,
42+ span_derived_primary_tags : Vec < ( & ' a str , & ' a str ) > ,
4243}
4344
4445impl hashbrown:: Equivalent < OwnedAggregationKey > for BorrowedAggregationKey < ' _ > {
@@ -59,6 +60,7 @@ impl hashbrown::Equivalent<OwnedAggregationKey> for BorrowedAggregationKey<'_> {
5960 http_endpoint,
6061 grpc_status_code,
6162 service_source,
63+ span_derived_primary_tags,
6264 } : & OwnedAggregationKey ,
6365 ) -> bool {
6466 self . resource_name == resource_name
@@ -79,6 +81,12 @@ impl hashbrown::Equivalent<OwnedAggregationKey> for BorrowedAggregationKey<'_> {
7981 && self . http_endpoint == http_endpoint
8082 && self . grpc_status_code == * grpc_status_code
8183 && self . service_source == service_source
84+ && self . span_derived_primary_tags . len ( ) == span_derived_primary_tags. len ( )
85+ && self
86+ . span_derived_primary_tags
87+ . iter ( )
88+ . zip ( span_derived_primary_tags. iter ( ) )
89+ . all ( |( ( k1, v1) , ( k2, v2) ) | k1 == k2 && v1 == v2)
8290 }
8391}
8492
@@ -104,6 +112,7 @@ pub(super) struct OwnedAggregationKey {
104112 http_endpoint : String ,
105113 grpc_status_code : Option < u8 > ,
106114 service_source : String ,
115+ span_derived_primary_tags : Vec < ( String , String ) > ,
107116}
108117
109118impl From < & BorrowedAggregationKey < ' _ > > for OwnedAggregationKey {
@@ -126,6 +135,11 @@ impl From<&BorrowedAggregationKey<'_>> for OwnedAggregationKey {
126135 http_endpoint : value. http_endpoint . to_owned ( ) ,
127136 grpc_status_code : value. grpc_status_code ,
128137 service_source : value. service_source . to_owned ( ) ,
138+ span_derived_primary_tags : value
139+ . span_derived_primary_tags
140+ . iter ( )
141+ . map ( |( k, v) | ( k. to_string ( ) , v. to_string ( ) ) )
142+ . collect ( ) ,
129143 }
130144 }
131145}
@@ -208,9 +222,16 @@ fn grpc_status_str_to_int_value(v: &str) -> Option<u8> {
208222impl < ' a > BorrowedAggregationKey < ' a > {
209223 /// Return an AggregationKey matching the given span.
210224 ///
211- /// If `peer_tags_keys ` is not empty then the peer tags of the span will be included in the
225+ /// If `peer_tag_keys ` is not empty then the peer tags of the span will be included in the
212226 /// key.
213- pub ( super ) fn from_span < T : StatSpan < ' a > > ( span : & ' a T , peer_tag_keys : & ' a [ String ] ) -> Self {
227+ ///
228+ /// If `span_derived_primary_tag_keys` is not empty then matching span tags keys are included
229+ /// in the key.
230+ pub ( super ) fn from_span < T : StatSpan < ' a > > (
231+ span : & ' a T ,
232+ peer_tag_keys : & ' a [ String ] ,
233+ span_derived_primary_tag_keys : & ' a [ String ] ,
234+ ) -> Self {
214235 let span_kind = span. get_meta ( TAG_SPANKIND ) . unwrap_or_default ( ) ;
215236 let peer_tags = if should_track_peer_tags ( span_kind) {
216237 // Parse the meta tags of the span and return a list of the peer tags based on the list
@@ -245,6 +266,11 @@ impl<'a> BorrowedAggregationKey<'a> {
245266
246267 let service_source = span. get_meta ( TAG_SVC_SRC ) . unwrap_or_default ( ) ;
247268
269+ let span_derived_primary_tags: Vec < ( & ' a str , & ' a str ) > = span_derived_primary_tag_keys
270+ . iter ( )
271+ . filter_map ( |key| Some ( ( ( key. as_str ( ) ) , ( span. get_meta ( key. as_str ( ) ) ?) ) ) )
272+ . collect ( ) ;
273+
248274 Self {
249275 resource_name : span. resource ( ) ,
250276 service_name : span. service ( ) ,
@@ -261,6 +287,7 @@ impl<'a> BorrowedAggregationKey<'a> {
261287 http_endpoint,
262288 grpc_status_code,
263289 service_source,
290+ span_derived_primary_tags,
264291 }
265292 }
266293}
@@ -288,6 +315,14 @@ impl From<pb::ClientGroupedStats> for OwnedAggregationKey {
288315 http_endpoint : value. http_endpoint ,
289316 grpc_status_code : value. grpc_status_code . parse ( ) . ok ( ) ,
290317 service_source : value. service_source ,
318+ span_derived_primary_tags : value
319+ . span_derived_primary_tags
320+ . into_iter ( )
321+ . filter_map ( |t| {
322+ let ( key, value) = t. split_once ( ':' ) ?;
323+ Some ( ( key. to_string ( ) , value. to_string ( ) ) )
324+ } )
325+ . collect ( ) ,
291326 }
292327 }
293328}
@@ -418,7 +453,11 @@ fn encode_grouped_stats(key: OwnedAggregationKey, group: GroupedStats) -> pb::Cl
418453 . map ( |c| c. to_string ( ) )
419454 . unwrap_or_default ( ) ,
420455 service_source : key. service_source ,
421- span_derived_primary_tags : vec ! [ ] , // Todo
456+ span_derived_primary_tags : key
457+ . span_derived_primary_tags
458+ . into_iter ( )
459+ . map ( |( k, v) | format ! ( "{k}:{v}" ) )
460+ . collect ( ) ,
422461 }
423462}
424463
@@ -820,6 +859,51 @@ mod tests {
820859 ) ,
821860 ] ;
822861
862+ let test_primary_tag_keys = vec ! [ "region" . to_string( ) , "env" . to_string( ) ] ;
863+ let test_cases_primary_tags: Vec < ( SpanSlice , OwnedAggregationKey ) > = vec ! [
864+ (
865+ SpanSlice {
866+ service: "service" ,
867+ name: "op" ,
868+ resource: "res" ,
869+ span_id: 1 ,
870+ parent_id: 0 ,
871+ meta: HashMap :: from( [ ( "region" , "us1" ) , ( "env" , "prod" ) ] ) ,
872+ ..Default :: default ( )
873+ } ,
874+ OwnedAggregationKey {
875+ service_name: "service" . into( ) ,
876+ operation_name: "op" . into( ) ,
877+ resource_name: "res" . into( ) ,
878+ is_trace_root: true ,
879+ span_derived_primary_tags: vec![
880+ ( "region" . into( ) , "us1" . into( ) ) ,
881+ ( "env" . into( ) , "prod" . into( ) ) ,
882+ ] ,
883+ ..Default :: default ( )
884+ } ,
885+ ) ,
886+ (
887+ SpanSlice {
888+ service: "service" ,
889+ name: "op" ,
890+ resource: "res" ,
891+ span_id: 1 ,
892+ parent_id: 0 ,
893+ meta: HashMap :: from( [ ( "region" , "us1" ) ] ) ,
894+ ..Default :: default ( )
895+ } ,
896+ OwnedAggregationKey {
897+ service_name: "service" . into( ) ,
898+ operation_name: "op" . into( ) ,
899+ resource_name: "res" . into( ) ,
900+ is_trace_root: true ,
901+ span_derived_primary_tags: vec![ ( "region" . into( ) , "us1" . into( ) ) ] ,
902+ ..Default :: default ( )
903+ } ,
904+ ) ,
905+ ] ;
906+
823907 let test_peer_tags = vec ! [
824908 "aws.s3.bucket" . to_string( ) ,
825909 "db.instance" . to_string( ) ,
@@ -907,7 +991,7 @@ mod tests {
907991 ] ;
908992
909993 for ( span, expected_key) in test_cases {
910- let borrowed_key = BorrowedAggregationKey :: from_span ( & span, & [ ] ) ;
994+ let borrowed_key = BorrowedAggregationKey :: from_span ( & span, & [ ] , & [ ] ) ;
911995 assert_eq ! (
912996 OwnedAggregationKey :: from( & borrowed_key) ,
913997 expected_key,
@@ -919,8 +1003,19 @@ mod tests {
9191003 ) ;
9201004 }
9211005
1006+ for ( span, expected_key) in test_cases_primary_tags {
1007+ let borrowed_key =
1008+ BorrowedAggregationKey :: from_span ( & span, & [ ] , test_primary_tag_keys. as_slice ( ) ) ;
1009+ assert_eq ! ( OwnedAggregationKey :: from( & borrowed_key) , expected_key) ;
1010+ assert_eq ! (
1011+ get_hash( & borrowed_key) ,
1012+ get_hash( & OwnedAggregationKey :: from( & borrowed_key) )
1013+ ) ;
1014+ }
1015+
9221016 for ( span, expected_key) in test_cases_with_peer_tags {
923- let borrowed_key = BorrowedAggregationKey :: from_span ( & span, test_peer_tags. as_slice ( ) ) ;
1017+ let borrowed_key =
1018+ BorrowedAggregationKey :: from_span ( & span, test_peer_tags. as_slice ( ) , & [ ] ) ;
9241019 assert_eq ! ( OwnedAggregationKey :: from( & borrowed_key) , expected_key) ;
9251020 assert_eq ! (
9261021 get_hash( & borrowed_key) ,
0 commit comments