1- use  crate :: exporter:: ModelConfig ; 
1+ use  crate :: { exporter:: ModelConfig ,   DatadogTraceState } ; 
22use  http:: uri; 
3+ use  opentelemetry:: Value ; 
34use  opentelemetry_sdk:: { 
45    trace:: { self ,  SpanData } , 
56    ExportError ,  Resource , 
@@ -14,15 +15,38 @@ use super::Mapping;
1415pub  mod  unified_tags; 
1516mod  v03; 
1617mod  v05; 
18+ mod  v07; 
1719
1820// todo: we should follow the same mapping defined in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/api/otlp.go 
1921
2022// https://github.com/DataDog/dd-trace-js/blob/c89a35f7d27beb4a60165409376e170eacb194c5/packages/dd-trace/src/constants.js#L4 
2123static  SAMPLING_PRIORITY_KEY :  & str  = "_sampling_priority_v1" ; 
2224
25+ #[ cfg( not( feature = "agent-sampling" ) ) ]  
26+ fn  get_sampling_priority ( _span :  & SpanData )  -> f64  { 
27+     1.0 
28+ } 
29+ 
30+ #[ cfg( feature = "agent-sampling" ) ]  
31+ fn  get_sampling_priority ( span :  & SpanData )  -> f64  { 
32+     if  span. span_context . trace_state ( ) . priority_sampling_enabled ( )  { 
33+         1.0 
34+     }  else  { 
35+         0.0 
36+     } 
37+ } 
38+ 
2339// https://github.com/DataDog/datadog-agent/blob/ec96f3c24173ec66ba235bda7710504400d9a000/pkg/trace/traceutil/span.go#L20 
2440static  DD_MEASURED_KEY :  & str  = "_dd.measured" ; 
2541
42+ fn  get_measuring ( span :  & SpanData )  -> f64  { 
43+     if  span. span_context . trace_state ( ) . measuring_enabled ( )  { 
44+         1.0 
45+     }  else  { 
46+         0.0 
47+     } 
48+ } 
49+ 
2650/// Custom mapping between opentelemetry spans and datadog spans. 
2751/// 
2852/// User can provide custom function to change the mapping. It currently supports customizing the following 
@@ -77,6 +101,16 @@ fn default_resource_mapping<'a>(span: &'a SpanData, _config: &'a ModelConfig) ->
77101    span. name . as_ref ( ) 
78102} 
79103
104+ fn  get_span_type ( span :  & SpanData )  -> Option < & Value >  { 
105+     for  kv in  & span. attributes  { 
106+         if  kv. key . as_str ( )  == "span.type"  { 
107+             return  Some ( & kv. value ) ; 
108+         } 
109+     } 
110+ 
111+     None 
112+ } 
113+ 
80114/// Wrap type for errors from opentelemetry datadog exporter 
81115#[ derive( Debug ,  thiserror:: Error ) ]  
82116pub  enum  Error  { 
@@ -129,20 +163,24 @@ pub enum ApiVersion {
129163Version03 , 
130164    /// Version 0.5 - requires datadog-agent v7.22.0 or above 
131165Version05 , 
166+     /// Version 0.7 
167+ Version07 , 
132168} 
133169
134170impl  ApiVersion  { 
135171    pub ( crate )  fn  path ( self )  -> & ' static  str  { 
136172        match  self  { 
137173            ApiVersion :: Version03  => "/v0.3/traces" , 
138174            ApiVersion :: Version05  => "/v0.5/traces" , 
175+             ApiVersion :: Version07  => "/v0.7/traces" , 
139176        } 
140177    } 
141178
142179    pub ( crate )  fn  content_type ( self )  -> & ' static  str  { 
143180        match  self  { 
144181            ApiVersion :: Version03  => "application/msgpack" , 
145182            ApiVersion :: Version05  => "application/msgpack" , 
183+             ApiVersion :: Version07  => "application/msgpack" , 
146184        } 
147185    } 
148186
@@ -190,6 +228,24 @@ impl ApiVersion {
190228                unified_tags, 
191229                resource, 
192230            ) , 
231+             Self :: Version07  => v07:: encode ( 
232+                 model_config, 
233+                 traces, 
234+                 |span,  config| match  & mapping. service_name  { 
235+                     Some ( f)  => f ( span,  config) , 
236+                     None  => default_service_name_mapping ( span,  config) , 
237+                 } , 
238+                 |span,  config| match  & mapping. name  { 
239+                     Some ( f)  => f ( span,  config) , 
240+                     None  => default_name_mapping ( span,  config) , 
241+                 } , 
242+                 |span,  config| match  & mapping. resource  { 
243+                     Some ( f)  => f ( span,  config) , 
244+                     None  => default_resource_mapping ( span,  config) , 
245+                 } , 
246+                 unified_tags, 
247+                 resource, 
248+             ) , 
193249        } 
194250    } 
195251} 
@@ -198,6 +254,7 @@ impl ApiVersion {
198254pub ( crate )  mod  tests { 
199255    use  super :: * ; 
200256    use  base64:: { engine:: general_purpose:: STANDARD ,  Engine } ; 
257+     use  opentelemetry:: trace:: Event ; 
201258    use  opentelemetry:: InstrumentationScope ; 
202259    use  opentelemetry:: { 
203260        trace:: { SpanContext ,  SpanId ,  SpanKind ,  Status ,  TraceFlags ,  TraceId ,  TraceState } , 
@@ -213,7 +270,43 @@ pub(crate) mod tests {
213270        vec ! [ vec![ get_span( 7 ,  1 ,  99 ) ] ] 
214271    } 
215272
273+     fn  get_traces_with_events ( )  -> Vec < Vec < trace:: SpanData > >  { 
274+         let  event = Event :: new ( 
275+             "myevent" , 
276+             SystemTime :: UNIX_EPOCH 
277+                 . checked_add ( Duration :: from_secs ( 5 ) ) 
278+                 . unwrap ( ) , 
279+             vec ! [ 
280+                 KeyValue :: new( "mykey" ,  1 ) , 
281+                 KeyValue :: new( 
282+                     "myarray" , 
283+                     Value :: Array ( opentelemetry:: Array :: String ( vec![ 
284+                         "myvalue1" . into( ) , 
285+                         "myvalue2" . into( ) , 
286+                     ] ) ) , 
287+                 ) , 
288+                 KeyValue :: new( "mybool" ,  true ) , 
289+                 KeyValue :: new( "myint" ,  2.5 ) , 
290+                 KeyValue :: new( "myboolfalse" ,  false ) , 
291+             ] , 
292+             0 , 
293+         ) ; 
294+         let  mut  events = SpanEvents :: default ( ) ; 
295+         events. events . push ( event) ; 
296+ 
297+         vec ! [ vec![ get_span_with_events( 7 ,  1 ,  99 ,  events) ] ] 
298+     } 
299+ 
216300    pub ( crate )  fn  get_span ( trace_id :  u128 ,  parent_span_id :  u64 ,  span_id :  u64 )  -> trace:: SpanData  { 
301+         get_span_with_events ( trace_id,  parent_span_id,  span_id,  SpanEvents :: default ( ) ) 
302+     } 
303+ 
304+     pub ( crate )  fn  get_span_with_events ( 
305+         trace_id :  u128 , 
306+         parent_span_id :  u64 , 
307+         span_id :  u64 , 
308+         events :  SpanEvents , 
309+     )  -> trace:: SpanData  { 
217310        let  span_context = SpanContext :: new ( 
218311            TraceId :: from_u128 ( trace_id) , 
219312            SpanId :: from_u64 ( span_id) , 
@@ -226,7 +319,6 @@ pub(crate) mod tests {
226319        let  end_time = start_time. checked_add ( Duration :: from_secs ( 1 ) ) . unwrap ( ) ; 
227320
228321        let  attributes = vec ! [ KeyValue :: new( "span.type" ,  "web" ) ] ; 
229-         let  events = SpanEvents :: default ( ) ; 
230322        let  links = SpanLinks :: default ( ) ; 
231323        let  instrumentation_scope = InstrumentationScope :: builder ( "component" ) . build ( ) ; 
232324
@@ -305,4 +397,71 @@ pub(crate) mod tests {
305397
306398        Ok ( ( ) ) 
307399    } 
400+ 
401+     #[ test]  
402+     fn  test_encode_v07 ( )  { 
403+         let  traces = get_traces_with_events ( ) ; 
404+         let  model_config = ModelConfig  { 
405+             service_name :  "service_name" . to_string ( ) , 
406+             ..Default :: default ( ) 
407+         } ; 
408+ 
409+         // we use an empty builder with a single attribute because the attributes are in a hashmap 
410+         // which causes the order to change every test 
411+         let  resource = Resource :: builder_empty ( ) 
412+             . with_attribute ( KeyValue :: new ( "host.name" ,  "test" ) ) 
413+             . build ( ) ; 
414+ 
415+         let  mut  unified_tags = UnifiedTags :: new ( ) ; 
416+         unified_tags. set_env ( Some ( String :: from ( "test-env" ) ) ) ; 
417+         unified_tags. set_version ( Some ( String :: from ( "test-version" ) ) ) ; 
418+         unified_tags. set_service ( Some ( String :: from ( "test-service" ) ) ) ; 
419+ 
420+         let  encoded = STANDARD . encode ( 
421+             ApiVersion :: Version07 
422+                 . encode ( 
423+                     & model_config, 
424+                     traces. iter ( ) . map ( |x| & x[ ..] ) . collect ( ) , 
425+                     & Mapping :: empty ( ) , 
426+                     & unified_tags, 
427+                     Some ( & resource) , 
428+                 ) 
429+                 . unwrap ( ) , 
430+         ) ; 
431+ 
432+         // A very nice way to check the encoded values is to use 
433+         // https://github.com/DataDog/dd-apm-test-agent 
434+         // Which is a test http server that receives and validates sent traces 
435+         let  expected = "ha1sYW5ndWFnZV9uYW1lpHJ1c3SmY2h1bmtzkYOocHJpb3JpdHnSAAAAAaZvcmlnaW6gpXNwY\  
436+ \ 
437+ \ 
438+ \ 
439+ \ 
440+ \ 
441+ \ 
442+ \ 
443+ \ 
444+ \ 
445+ ; 
446+         assert_eq ! ( encoded. as_str( ) ,  expected) ; 
447+ 
448+         // change to a different resource and make sure the encoded value changes and that we actually encode stuff 
449+         let  other_resource = Resource :: builder_empty ( ) 
450+             . with_attribute ( KeyValue :: new ( "host.name" ,  "thisissometingelse" ) ) 
451+             . build ( ) ; 
452+ 
453+         let  encoded = STANDARD . encode ( 
454+             ApiVersion :: Version07 
455+                 . encode ( 
456+                     & model_config, 
457+                     traces. iter ( ) . map ( |x| & x[ ..] ) . collect ( ) , 
458+                     & Mapping :: empty ( ) , 
459+                     & unified_tags, 
460+                     Some ( & other_resource) , 
461+                 ) 
462+                 . unwrap ( ) , 
463+         ) ; 
464+ 
465+         assert_ne ! ( encoded. as_str( ) ,  expected) ; 
466+     } 
308467} 
0 commit comments