@@ -24,13 +24,17 @@ use async_trait::async_trait;
24
24
use futures:: stream:: StreamExt ;
25
25
use opentelemetry:: {
26
26
global:: handle_error,
27
- sdk:: export:: {
28
- trace:: { ExportResult , SpanData , SpanExporter } ,
29
- ExportError ,
27
+ sdk:: {
28
+ export:: {
29
+ trace:: { ExportResult , SpanData , SpanExporter } ,
30
+ ExportError ,
31
+ } ,
32
+ trace:: EvictedHashMap ,
30
33
} ,
31
34
trace:: TraceError ,
32
35
Value ,
33
36
} ;
37
+ use opentelemetry_semantic_conventions as semcov;
34
38
use thiserror:: Error ;
35
39
#[ cfg( any( feature = "yup-authorizer" , feature = "gcp_auth" ) ) ]
36
40
use tonic:: metadata:: MetadataValue ;
@@ -240,12 +244,6 @@ where
240
244
let mut entries = Vec :: new ( ) ;
241
245
let mut spans = Vec :: with_capacity ( batch. len ( ) ) ;
242
246
for span in batch {
243
- let attribute_map = span
244
- . attributes
245
- . into_iter ( )
246
- . map ( |( key, value) | ( key. as_str ( ) . to_owned ( ) , value. into ( ) ) )
247
- . collect ( ) ;
248
-
249
247
let trace_id = hex:: encode ( span. span_context . trace_id ( ) . to_bytes ( ) ) ;
250
248
let span_id = hex:: encode ( span. span_context . span_id ( ) . to_bytes ( ) ) ;
251
249
let time_event = match & self . log_client {
@@ -321,10 +319,7 @@ where
321
319
parent_span_id : hex:: encode ( span. parent_span_id . to_bytes ( ) ) ,
322
320
start_time : Some ( span. start_time . into ( ) ) ,
323
321
end_time : Some ( span. end_time . into ( ) ) ,
324
- attributes : Some ( Attributes {
325
- attribute_map,
326
- ..Default :: default ( )
327
- } ) ,
322
+ attributes : Some ( span. attributes . into ( ) ) ,
328
323
time_events : Some ( TimeEvents {
329
324
time_event,
330
325
..Default :: default ( )
@@ -657,3 +652,207 @@ pub enum MonitoredResource {
657
652
658
653
const TRACE_APPEND : & str = "https://www.googleapis.com/auth/trace.append" ;
659
654
const LOGGING_WRITE : & str = "https://www.googleapis.com/auth/logging.write" ;
655
+ const HTTP_PATH_ATTRIBUTE : & str = "http.path" ;
656
+
657
+ const GCP_HTTP_HOST : & str = "/http/host" ;
658
+ const GCP_HTTP_METHOD : & str = "/http/method" ;
659
+ const GCP_HTTP_TARGET : & str = "/http/path" ;
660
+ const GCP_HTTP_URL : & str = "/http/url" ;
661
+ const GCP_HTTP_USER_AGENT : & str = "/http/user_agent" ;
662
+ const GCP_HTTP_STATUS_CODE : & str = "/http/status_code" ;
663
+ const GCP_HTTP_ROUTE : & str = "/http/route" ;
664
+ const GCP_HTTP_PATH : & str = "/http/path" ;
665
+ const GCP_SERVICE_NAME : & str = "g.co/gae/app/module" ;
666
+
667
+ impl From < EvictedHashMap > for Attributes {
668
+ fn from ( attributes : EvictedHashMap ) -> Self {
669
+ let mut dropped_attributes_count: i32 = 0 ;
670
+ let attribute_map = attributes
671
+ . into_iter ( )
672
+ . flat_map ( |( k, v) | {
673
+ let key = k. as_str ( ) ;
674
+ if key. len ( ) > 128 {
675
+ dropped_attributes_count += 1 ;
676
+ return None ;
677
+ }
678
+
679
+ if semcov:: trace:: HTTP_HOST == k {
680
+ return Some ( ( GCP_HTTP_HOST . to_owned ( ) , v. into ( ) ) ) ;
681
+ }
682
+
683
+ if semcov:: trace:: HTTP_METHOD == k {
684
+ return Some ( ( GCP_HTTP_METHOD . to_owned ( ) , v. into ( ) ) ) ;
685
+ }
686
+
687
+ if semcov:: trace:: HTTP_TARGET == k {
688
+ return Some ( ( GCP_HTTP_TARGET . to_owned ( ) , v. into ( ) ) ) ;
689
+ }
690
+
691
+ if semcov:: trace:: HTTP_URL == k {
692
+ return Some ( ( GCP_HTTP_URL . to_owned ( ) , v. into ( ) ) ) ;
693
+ }
694
+
695
+ if semcov:: trace:: HTTP_USER_AGENT == k {
696
+ return Some ( ( GCP_HTTP_USER_AGENT . to_owned ( ) , v. into ( ) ) ) ;
697
+ }
698
+
699
+ if semcov:: trace:: HTTP_STATUS_CODE == k {
700
+ return Some ( ( GCP_HTTP_STATUS_CODE . to_owned ( ) , v. into ( ) ) ) ;
701
+ }
702
+
703
+ if semcov:: trace:: HTTP_ROUTE == k {
704
+ return Some ( ( GCP_HTTP_ROUTE . to_owned ( ) , v. into ( ) ) ) ;
705
+ } ;
706
+
707
+ if semcov:: resource:: SERVICE_NAME == k {
708
+ return Some ( ( GCP_SERVICE_NAME . to_owned ( ) , v. into ( ) ) ) ;
709
+ } ;
710
+
711
+ if HTTP_PATH_ATTRIBUTE == key {
712
+ return Some ( ( GCP_HTTP_PATH . to_owned ( ) , v. into ( ) ) ) ;
713
+ }
714
+
715
+ Some ( ( key. to_owned ( ) , v. into ( ) ) )
716
+ } )
717
+ . collect ( ) ;
718
+ Attributes {
719
+ attribute_map,
720
+ dropped_attributes_count,
721
+ }
722
+ }
723
+ }
724
+
725
+ #[ cfg( test) ]
726
+ mod tests {
727
+ use super :: * ;
728
+
729
+ use opentelemetry:: { sdk:: trace:: EvictedHashMap , KeyValue , Value } ;
730
+ use opentelemetry_semantic_conventions as semcov;
731
+
732
+ #[ test]
733
+ fn test_attributes_mapping ( ) {
734
+ let capacity = 10 ;
735
+ let mut attributes = EvictedHashMap :: new ( capacity, 0 ) ;
736
+
737
+ // hostAttribute = "http.host"
738
+ attributes. insert ( semcov:: trace:: HTTP_HOST . string ( "example.com:8080" ) ) ;
739
+
740
+ // methodAttribute = "http.method"
741
+ attributes. insert ( semcov:: trace:: HTTP_METHOD . string ( "POST" ) ) ;
742
+
743
+ // pathAttribute = "http.path"
744
+ attributes. insert ( KeyValue :: new (
745
+ "http.path" ,
746
+ Value :: String ( "/path/12314/?q=ddds#123" . into ( ) ) ,
747
+ ) ) ;
748
+
749
+ // urlAttribute = "http.url"
750
+ attributes. insert (
751
+ semcov:: trace:: HTTP_URL . string ( "https://example.com:8080/webshop/articles/4?s=1" ) ,
752
+ ) ;
753
+
754
+ // userAgentAttribute = "http.user_agent"
755
+ attributes
756
+ . insert ( semcov:: trace:: HTTP_USER_AGENT . string ( "CERN-LineMode/2.15 libwww/2.17b3" ) ) ;
757
+
758
+ // statusCodeAttribute = "http.status_code"
759
+ attributes. insert ( semcov:: trace:: HTTP_STATUS_CODE . i64 ( 200 ) ) ;
760
+
761
+ // statusCodeAttribute = "http.route"
762
+ attributes. insert ( semcov:: trace:: HTTP_ROUTE . string ( "/webshop/articles/:article_id" ) ) ;
763
+
764
+ // serviceAttribute = "service.name"
765
+ attributes. insert ( semcov:: resource:: SERVICE_NAME . string ( "Test Service Name" ) ) ;
766
+
767
+ let actual: Attributes = attributes. into ( ) ;
768
+
769
+ assert_eq ! ( actual. attribute_map. len( ) , 8 ) ;
770
+ assert_eq ! ( actual. dropped_attributes_count, 0 ) ;
771
+ assert_eq ! (
772
+ actual. attribute_map. get( "/http/host" ) ,
773
+ Some ( & AttributeValue :: from( Value :: String (
774
+ "example.com:8080" . into( )
775
+ ) ) )
776
+ ) ;
777
+ assert_eq ! (
778
+ actual. attribute_map. get( "/http/method" ) ,
779
+ Some ( & AttributeValue :: from( Value :: String ( "POST" . into( ) ) ) ) ,
780
+ ) ;
781
+ assert_eq ! (
782
+ actual. attribute_map. get( "/http/path" ) ,
783
+ Some ( & AttributeValue :: from( Value :: String (
784
+ "/path/12314/?q=ddds#123" . into( )
785
+ ) ) ) ,
786
+ ) ;
787
+ assert_eq ! (
788
+ actual. attribute_map. get( "/http/route" ) ,
789
+ Some ( & AttributeValue :: from( Value :: String (
790
+ "/webshop/articles/:article_id" . into( )
791
+ ) ) ) ,
792
+ ) ;
793
+ assert_eq ! (
794
+ actual. attribute_map. get( "/http/url" ) ,
795
+ Some ( & AttributeValue :: from( Value :: String (
796
+ "https://example.com:8080/webshop/articles/4?s=1" . into( ) ,
797
+ ) ) ) ,
798
+ ) ;
799
+ assert_eq ! (
800
+ actual. attribute_map. get( "/http/user_agent" ) ,
801
+ Some ( & AttributeValue :: from( Value :: String (
802
+ "CERN-LineMode/2.15 libwww/2.17b3" . into( )
803
+ ) ) ) ,
804
+ ) ;
805
+ assert_eq ! (
806
+ actual. attribute_map. get( "/http/status_code" ) ,
807
+ Some ( & AttributeValue :: from( Value :: I64 ( 200 ) ) ) ,
808
+ ) ;
809
+ assert_eq ! (
810
+ actual. attribute_map. get( "g.co/gae/app/module" ) ,
811
+ Some ( & AttributeValue :: from( Value :: String (
812
+ "Test Service Name" . into( )
813
+ ) ) ) ,
814
+ ) ;
815
+ }
816
+
817
+ #[ test]
818
+ fn test_attributes_mapping_http_target ( ) {
819
+ let capacity = 10 ;
820
+ let mut attributes = EvictedHashMap :: new ( capacity, 0 ) ;
821
+
822
+ // hostAttribute = "http.target"
823
+ attributes. insert ( semcov:: trace:: HTTP_TARGET . string ( "/path/12314/?q=ddds#123" ) ) ;
824
+
825
+ let actual: Attributes = attributes. into ( ) ;
826
+
827
+ assert_eq ! ( actual. attribute_map. len( ) , 1 ) ;
828
+ assert_eq ! ( actual. dropped_attributes_count, 0 ) ;
829
+ assert_eq ! (
830
+ actual. attribute_map. get( "/http/path" ) ,
831
+ Some ( & AttributeValue :: from( Value :: String (
832
+ "/path/12314/?q=ddds#123" . into( )
833
+ ) ) ) ,
834
+ ) ;
835
+ }
836
+
837
+ #[ test]
838
+ fn test_attributes_mapping_dropped_attributes_count ( ) {
839
+ let capacity = 10 ;
840
+ let mut attributes = EvictedHashMap :: new ( capacity, 0 ) ;
841
+ attributes. insert ( KeyValue :: new ( "answer" , Value :: I64 ( 42 ) ) ) ;
842
+ attributes. insert ( KeyValue :: new ( "long_attribute_key_dvwmacxpeefbuemoxljmqvldjxmvvihoeqnuqdsyovwgljtnemouidabhkmvsnauwfnaihekcfwhugejboiyfthyhmkpsaxtidlsbwsmirebax" , Value :: String ( "Some value" . into ( ) ) ) ) ;
843
+
844
+ let actual: Attributes = attributes. into ( ) ;
845
+ assert_eq ! (
846
+ actual,
847
+ Attributes {
848
+ attribute_map: HashMap :: from( [ (
849
+ "answer" . into( ) ,
850
+ AttributeValue :: from( Value :: I64 ( 42 ) )
851
+ ) , ] ) ,
852
+ dropped_attributes_count: 1 ,
853
+ }
854
+ ) ;
855
+ assert_eq ! ( actual. attribute_map. len( ) , 1 ) ;
856
+ assert_eq ! ( actual. dropped_attributes_count, 1 ) ;
857
+ }
858
+ }
0 commit comments