@@ -3,7 +3,11 @@ mod model;
3
3
4
4
pub use model:: ApiVersion ;
5
5
pub use model:: Error ;
6
+ pub use model:: FieldMappingFn ;
6
7
8
+ use std:: fmt:: { Debug , Formatter } ;
9
+
10
+ use crate :: exporter:: model:: FieldMapping ;
7
11
use async_trait:: async_trait;
8
12
use http:: { Method , Request , Uri } ;
9
13
use itertools:: Itertools ;
@@ -27,43 +31,72 @@ const DEFAULT_AGENT_ENDPOINT: &str = "http://127.0.0.1:8126";
27
31
const DATADOG_TRACE_COUNT_HEADER : & str = "X-Datadog-Trace-Count" ;
28
32
29
33
/// Datadog span exporter
30
- #[ derive( Debug ) ]
31
34
pub struct DatadogExporter {
32
35
client : Box < dyn HttpClient > ,
33
36
request_url : Uri ,
34
- service_name : String ,
37
+ model_config : ModelConfig ,
35
38
version : ApiVersion ,
39
+
40
+ resource_mapping : Option < FieldMapping > ,
41
+ name_mapping : Option < FieldMapping > ,
42
+ service_name_mapping : Option < FieldMapping > ,
36
43
}
37
44
38
45
impl DatadogExporter {
39
46
fn new (
40
- service_name : String ,
47
+ model_config : ModelConfig ,
41
48
request_url : Uri ,
42
49
version : ApiVersion ,
43
50
client : Box < dyn HttpClient > ,
51
+ resource_mapping : Option < FieldMapping > ,
52
+ name_mapping : Option < FieldMapping > ,
53
+ service_name_mapping : Option < FieldMapping > ,
44
54
) -> Self {
45
55
DatadogExporter {
46
56
client,
47
57
request_url,
48
- service_name ,
58
+ model_config ,
49
59
version,
60
+ resource_mapping,
61
+ name_mapping,
62
+ service_name_mapping,
50
63
}
51
64
}
52
65
}
53
66
67
+ impl Debug for DatadogExporter {
68
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
69
+ f. debug_struct ( "DatadogExporter" )
70
+ . field ( "model_config" , & self . model_config )
71
+ . field ( "request_url" , & self . request_url )
72
+ . field ( "version" , & self . version )
73
+ . field ( "client" , & self . client )
74
+ . field ( "resource_mapping" , & mapping_debug ( & self . resource_mapping ) )
75
+ . field ( "name_mapping" , & mapping_debug ( & self . name_mapping ) )
76
+ . field (
77
+ "service_name_mapping" ,
78
+ & mapping_debug ( & self . service_name_mapping ) ,
79
+ )
80
+ . finish ( )
81
+ }
82
+ }
83
+
54
84
/// Create a new Datadog exporter pipeline builder.
55
85
pub fn new_pipeline ( ) -> DatadogPipelineBuilder {
56
86
DatadogPipelineBuilder :: default ( )
57
87
}
58
88
59
89
/// Builder for `ExporterConfig` struct.
60
- #[ derive( Debug ) ]
61
90
pub struct DatadogPipelineBuilder {
62
91
service_name : Option < String > ,
63
92
agent_endpoint : String ,
64
93
trace_config : Option < sdk:: trace:: Config > ,
65
94
version : ApiVersion ,
66
95
client : Option < Box < dyn HttpClient > > ,
96
+
97
+ resource_mapping : Option < FieldMapping > ,
98
+ name_mapping : Option < FieldMapping > ,
99
+ service_name_mapping : Option < FieldMapping > ,
67
100
}
68
101
69
102
impl Default for DatadogPipelineBuilder {
@@ -72,6 +105,9 @@ impl Default for DatadogPipelineBuilder {
72
105
service_name : None ,
73
106
agent_endpoint : DEFAULT_AGENT_ENDPOINT . to_string ( ) ,
74
107
trace_config : None ,
108
+ resource_mapping : None ,
109
+ name_mapping : None ,
110
+ service_name_mapping : None ,
75
111
version : ApiVersion :: Version05 ,
76
112
#[ cfg( all(
77
113
not( feature = "reqwest-client" ) ,
@@ -97,6 +133,24 @@ impl Default for DatadogPipelineBuilder {
97
133
}
98
134
}
99
135
136
+ impl Debug for DatadogPipelineBuilder {
137
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
138
+ f. debug_struct ( "DatadogExporter" )
139
+ . field ( "service_name" , & self . service_name )
140
+ . field ( "agent_endpoint" , & self . agent_endpoint )
141
+ . field ( "version" , & self . version )
142
+ . field ( "trace_config" , & self . trace_config )
143
+ . field ( "client" , & self . client )
144
+ . field ( "resource_mapping" , & mapping_debug ( & self . resource_mapping ) )
145
+ . field ( "name_mapping" , & mapping_debug ( & self . name_mapping ) )
146
+ . field (
147
+ "service_name_mapping" ,
148
+ & mapping_debug ( & self . service_name_mapping ) ,
149
+ )
150
+ . finish ( )
151
+ }
152
+ }
153
+
100
154
impl DatadogPipelineBuilder {
101
155
/// Building a new exporter.
102
156
///
@@ -148,12 +202,19 @@ impl DatadogPipelineBuilder {
148
202
service_name : String ,
149
203
) -> Result < DatadogExporter , TraceError > {
150
204
if let Some ( client) = self . client {
205
+ let model_config = ModelConfig {
206
+ service_name,
207
+ ..Default :: default ( )
208
+ } ;
151
209
let endpoint = self . agent_endpoint + self . version . path ( ) ;
152
210
let exporter = DatadogExporter :: new (
153
- service_name ,
211
+ model_config ,
154
212
endpoint. parse ( ) . map_err :: < Error , _ > ( Into :: into) ?,
155
213
self . version ,
156
214
client,
215
+ self . resource_mapping ,
216
+ self . name_mapping ,
217
+ self . service_name_mapping ,
157
218
) ;
158
219
Ok ( exporter)
159
220
} else {
@@ -231,6 +292,36 @@ impl DatadogPipelineBuilder {
231
292
self . version = version;
232
293
self
233
294
}
295
+
296
+ /// Custom the value used for `resource` field in datadog spans.
297
+ /// See [`FieldMappingFn`] for details.
298
+ pub fn with_resource_mapping < F > ( mut self , f : F ) -> Self
299
+ where
300
+ F : for < ' a > Fn ( & ' a SpanData , & ' a ModelConfig ) -> & ' a str + Send + Sync + ' static ,
301
+ {
302
+ self . resource_mapping = Some ( Arc :: new ( f) ) ;
303
+ self
304
+ }
305
+
306
+ /// Custom the value used for `name` field in datadog spans.
307
+ /// See [`FieldMappingFn`] for details.
308
+ pub fn with_name_mapping < F > ( mut self , f : F ) -> Self
309
+ where
310
+ F : for < ' a > Fn ( & ' a SpanData , & ' a ModelConfig ) -> & ' a str + Send + Sync + ' static ,
311
+ {
312
+ self . name_mapping = Some ( Arc :: new ( f) ) ;
313
+ self
314
+ }
315
+
316
+ /// Custom the value used for `service_name` field in datadog spans.
317
+ /// See [`FieldMappingFn`] for details.
318
+ pub fn with_service_name_mapping < F > ( mut self , f : F ) -> Self
319
+ where
320
+ F : for < ' a > Fn ( & ' a SpanData , & ' a ModelConfig ) -> & ' a str + Send + Sync + ' static ,
321
+ {
322
+ self . service_name_mapping = Some ( Arc :: new ( f) ) ;
323
+ self
324
+ }
234
325
}
235
326
236
327
fn group_into_traces ( spans : Vec < SpanData > ) -> Vec < Vec < SpanData > > {
@@ -248,7 +339,13 @@ impl trace::SpanExporter for DatadogExporter {
248
339
async fn export ( & mut self , batch : Vec < SpanData > ) -> trace:: ExportResult {
249
340
let traces: Vec < Vec < SpanData > > = group_into_traces ( batch) ;
250
341
let trace_count = traces. len ( ) ;
251
- let data = self . version . encode ( & self . service_name , traces) ?;
342
+ let data = self . version . encode (
343
+ & self . model_config ,
344
+ traces,
345
+ self . service_name_mapping . clone ( ) ,
346
+ self . name_mapping . clone ( ) ,
347
+ self . resource_mapping . clone ( ) ,
348
+ ) ?;
252
349
let req = Request :: builder ( )
253
350
. method ( Method :: POST )
254
351
. uri ( self . request_url . clone ( ) )
@@ -261,6 +358,24 @@ impl trace::SpanExporter for DatadogExporter {
261
358
}
262
359
}
263
360
361
+ /// Helper struct to custom the mapping between Opentelemetry spans and datadog spans.
362
+ ///
363
+ /// This struct will be passed to [`FieldMappingFn`]
364
+ #[ derive( Default , Debug ) ]
365
+ #[ non_exhaustive]
366
+ pub struct ModelConfig {
367
+ pub service_name : String ,
368
+ }
369
+
370
+ fn mapping_debug ( f : & Option < FieldMapping > ) -> String {
371
+ if f. is_some ( ) {
372
+ "custom mapping"
373
+ } else {
374
+ "default mapping"
375
+ }
376
+ . to_string ( )
377
+ }
378
+
264
379
#[ cfg( test) ]
265
380
mod tests {
266
381
use super :: * ;
0 commit comments