1
- use crate :: exporter:: ModelConfig ;
1
+ use crate :: { exporter:: ModelConfig , DatadogTraceState } ;
2
2
use http:: uri;
3
+ use opentelemetry:: Value ;
3
4
use opentelemetry_sdk:: {
4
5
trace:: { self , SpanData } ,
5
6
ExportError , Resource ,
@@ -14,15 +15,38 @@ use super::Mapping;
14
15
pub mod unified_tags;
15
16
mod v03;
16
17
mod v05;
18
+ mod v07;
17
19
18
20
// todo: we should follow the same mapping defined in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/api/otlp.go
19
21
20
22
// https://github.com/DataDog/dd-trace-js/blob/c89a35f7d27beb4a60165409376e170eacb194c5/packages/dd-trace/src/constants.js#L4
21
23
static SAMPLING_PRIORITY_KEY : & str = "_sampling_priority_v1" ;
22
24
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
+
23
39
// https://github.com/DataDog/datadog-agent/blob/ec96f3c24173ec66ba235bda7710504400d9a000/pkg/trace/traceutil/span.go#L20
24
40
static DD_MEASURED_KEY : & str = "_dd.measured" ;
25
41
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
+
26
50
/// Custom mapping between opentelemetry spans and datadog spans.
27
51
///
28
52
/// 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) ->
77
101
span. name . as_ref ( )
78
102
}
79
103
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
+
80
114
/// Wrap type for errors from opentelemetry datadog exporter
81
115
#[ derive( Debug , thiserror:: Error ) ]
82
116
pub enum Error {
@@ -129,20 +163,24 @@ pub enum ApiVersion {
129
163
Version03 ,
130
164
/// Version 0.5 - requires datadog-agent v7.22.0 or above
131
165
Version05 ,
166
+ /// Version 0.7
167
+ Version07 ,
132
168
}
133
169
134
170
impl ApiVersion {
135
171
pub ( crate ) fn path ( self ) -> & ' static str {
136
172
match self {
137
173
ApiVersion :: Version03 => "/v0.3/traces" ,
138
174
ApiVersion :: Version05 => "/v0.5/traces" ,
175
+ ApiVersion :: Version07 => "/v0.7/traces" ,
139
176
}
140
177
}
141
178
142
179
pub ( crate ) fn content_type ( self ) -> & ' static str {
143
180
match self {
144
181
ApiVersion :: Version03 => "application/msgpack" ,
145
182
ApiVersion :: Version05 => "application/msgpack" ,
183
+ ApiVersion :: Version07 => "application/msgpack" ,
146
184
}
147
185
}
148
186
@@ -190,6 +228,24 @@ impl ApiVersion {
190
228
unified_tags,
191
229
resource,
192
230
) ,
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
+ ) ,
193
249
}
194
250
}
195
251
}
@@ -198,6 +254,7 @@ impl ApiVersion {
198
254
pub ( crate ) mod tests {
199
255
use super :: * ;
200
256
use base64:: { engine:: general_purpose:: STANDARD , Engine } ;
257
+ use opentelemetry:: trace:: Event ;
201
258
use opentelemetry:: InstrumentationScope ;
202
259
use opentelemetry:: {
203
260
trace:: { SpanContext , SpanId , SpanKind , Status , TraceFlags , TraceId , TraceState } ,
@@ -213,7 +270,43 @@ pub(crate) mod tests {
213
270
vec ! [ vec![ get_span( 7 , 1 , 99 ) ] ]
214
271
}
215
272
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
+
216
300
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 {
217
310
let span_context = SpanContext :: new (
218
311
TraceId :: from_u128 ( trace_id) ,
219
312
SpanId :: from_u64 ( span_id) ,
@@ -226,7 +319,6 @@ pub(crate) mod tests {
226
319
let end_time = start_time. checked_add ( Duration :: from_secs ( 1 ) ) . unwrap ( ) ;
227
320
228
321
let attributes = vec ! [ KeyValue :: new( "span.type" , "web" ) ] ;
229
- let events = SpanEvents :: default ( ) ;
230
322
let links = SpanLinks :: default ( ) ;
231
323
let instrumentation_scope = InstrumentationScope :: builder ( "component" ) . build ( ) ;
232
324
@@ -305,4 +397,71 @@ pub(crate) mod tests {
305
397
306
398
Ok ( ( ) )
307
399
}
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
+ }
308
467
}
0 commit comments