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 {
129163 Version03 ,
130164 /// Version 0.5 - requires datadog-agent v7.22.0 or above
131165 Version05 ,
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+ W5zkY6kbmFtZaljb21wb25lbnSnc3Bhbl9pZM8AAAAAAAAAY6h0cmFjZV9pZM8AAAAAAAAAB6VzdGFydNMAAAAAAAAAAKhk\
437+ dXJhdGlvbtMAAAAAO5rKAKlwYXJlbnRfaWTPAAAAAAAAAAGnc2VydmljZaxzZXJ2aWNlX25hbWWocmVzb3VyY2WocmVzb3V\
438+ yY2WkdHlwZaN3ZWKlZXJyb3LSAAAAAKRtZXRhgqlob3N0Lm5hbWWkdGVzdKlzcGFuLnR5cGWjd2Vip21ldHJpY3OCtV9zYW\
439+ 1wbGluZ19wcmlvcml0eV92Mcs/8AAAAAAAAKxfZGQubWVhc3VyZWTLAAAAAAAAAACqc3Bhbl9saW5rc5Crc3Bhbl9ldmVud\
440+ HORg6RuYW1lp215ZXZlbnSudGltZV91bml4X25hbm/TAAAAASoF8gCqYXR0cmlidXRlc4WlbXlrZXmCpHR5cGXSAAAAAqlp\
441+ bnRfdmFsdWXTAAAAAAAAAAGnbXlhcnJheYKkdHlwZdIAAAAEq2FycmF5X3ZhbHVlkoKkdHlwZQCsc3RyaW5nX3ZhbHVlqG1\
442+ 5dmFsdWUxgqR0eXBlAKxzdHJpbmdfdmFsdWWobXl2YWx1ZTKmbXlib29sgqR0eXBl0gAAAAGqYm9vbF92YWx1ZcOlbXlpbn\
443+ SCpHR5cGXSAAAAA6xkb3VibGVfdmFsdWXLQAQAAAAAAACrbXlib29sZmFsc2WCpHR5cGXSAAAAAapib29sX3ZhbHVlwqR0Y\
444+ Wdzg6dzZXJ2aWNlrHRlc3Qtc2VydmljZad2ZXJzaW9urHRlc3QtdmVyc2lvbqNlbnaodGVzdC1lbnajZW52qHRlc3QtZW52\
445+ q2FwcF92ZXJzaW9urHRlc3QtdmVyc2lvbg==";
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