1
- use std:: { net:: SocketAddr , sync:: Arc , time:: Duration } ;
1
+ use std:: { fmt , net:: SocketAddr , sync:: Arc , time:: Duration } ;
2
2
3
3
use thiserror:: Error ;
4
+ use tracing:: debug;
4
5
5
6
use crate :: {
6
7
forwarder:: { self , ForwarderConfiguration , RemoteAddr } ,
@@ -10,13 +11,13 @@ use crate::{
10
11
11
12
// Maximum data length for a UDP datagram.
12
13
//
13
- // Realistically, users should basically never send payloads anywhere _near_ this large, but we're only trying to ensure
14
- // we're not about to do anything that we _know_ is technically invalid.
14
+ // Realistically, users should never send payloads anywhere _near_ this large, but we're only trying to ensure we're not
15
+ // about to do anything that we _know_ is technically invalid.
15
16
const UDP_DATAGRAM_MAX_PAYLOAD_LEN : usize = ( u16:: MAX as usize ) - 8 ;
16
17
17
18
const DEFAULT_WRITE_TIMEOUT : Duration = Duration :: from_secs ( 1 ) ;
18
- const DEFAULT_MAX_PAYLOAD_LEN : usize = 8192 ;
19
- const DEFAULT_FLUSH_INTERVAL : Duration = Duration :: from_secs ( 3 ) ;
19
+ const DEFAULT_FLUSH_INTERVAL_CONSERVATIVE : Duration = Duration :: from_secs ( 3 ) ;
20
+ const DEFAULT_FLUSH_INTERVAL_AGGRESSIVE : Duration = Duration :: from_secs ( 10 ) ;
20
21
const DEFAULT_HISTOGRAM_RESERVOIR_SIZE : usize = 1024 ;
21
22
22
23
/// Errors that could occur while building or installing a DogStatsD recorder/exporter.
@@ -65,13 +66,31 @@ pub enum AggregationMode {
65
66
Aggressive ,
66
67
}
67
68
69
+ impl AggregationMode {
70
+ fn default_flush_interval ( & self ) -> Duration {
71
+ match self {
72
+ AggregationMode :: Conservative => DEFAULT_FLUSH_INTERVAL_CONSERVATIVE ,
73
+ AggregationMode :: Aggressive => DEFAULT_FLUSH_INTERVAL_AGGRESSIVE ,
74
+ }
75
+ }
76
+ }
77
+
78
+ impl fmt:: Display for AggregationMode {
79
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
80
+ match self {
81
+ AggregationMode :: Conservative => write ! ( f, "conservative" ) ,
82
+ AggregationMode :: Aggressive => write ! ( f, "aggressive" ) ,
83
+ }
84
+ }
85
+ }
86
+
68
87
/// Builder for a DogStatsD exporter.
69
88
#[ derive( Debug ) ]
70
89
pub struct DogStatsDBuilder {
71
90
remote_addr : RemoteAddr ,
72
91
write_timeout : Duration ,
73
- max_payload_len : usize ,
74
- flush_interval : Duration ,
92
+ max_payload_len : Option < usize > ,
93
+ flush_interval : Option < Duration > ,
75
94
synchronous : bool ,
76
95
agg_mode : AggregationMode ,
77
96
telemetry : bool ,
@@ -81,20 +100,30 @@ pub struct DogStatsDBuilder {
81
100
}
82
101
83
102
impl DogStatsDBuilder {
103
+ fn get_max_payload_len ( & self ) -> usize {
104
+ self . max_payload_len . unwrap_or_else ( || self . remote_addr . default_max_payload_len ( ) )
105
+ }
106
+
107
+ fn get_flush_interval ( & self ) -> Duration {
108
+ self . flush_interval . unwrap_or_else ( || self . agg_mode . default_flush_interval ( ) )
109
+ }
110
+
84
111
fn validate_max_payload_len ( & self ) -> Result < ( ) , BuildError > {
112
+ let max_payload_len = self . get_max_payload_len ( ) ;
113
+
85
114
if let RemoteAddr :: Udp ( _) = & self . remote_addr {
86
- if self . max_payload_len > UDP_DATAGRAM_MAX_PAYLOAD_LEN {
115
+ if max_payload_len > UDP_DATAGRAM_MAX_PAYLOAD_LEN {
87
116
return Err ( BuildError :: InvalidConfiguration {
88
- reason : format ! ( "maximum payload length ({} bytes) exceeds UDP datagram maximum length ({} bytes)" , self . max_payload_len, UDP_DATAGRAM_MAX_PAYLOAD_LEN ) ,
117
+ reason : format ! ( "maximum payload length ({} bytes) exceeds UDP datagram maximum length ({} bytes)" , max_payload_len, UDP_DATAGRAM_MAX_PAYLOAD_LEN ) ,
89
118
} ) ;
90
119
}
91
120
}
92
121
93
- if self . max_payload_len > u32:: MAX as usize {
122
+ if max_payload_len > u32:: MAX as usize {
94
123
return Err ( BuildError :: InvalidConfiguration {
95
124
reason : format ! (
96
125
"maximum payload length ({} bytes) exceeds theoretical upper bound ({} bytes)" ,
97
- self . max_payload_len,
126
+ max_payload_len,
98
127
u32 :: MAX
99
128
) ,
100
129
} ) ;
@@ -146,7 +175,7 @@ impl DogStatsDBuilder {
146
175
/// Setting a higher value is likely to lead to invalid metric payloads that are discarded by the Datadog Agent when
147
176
/// received.
148
177
///
149
- /// Defaults to 8192 bytes.
178
+ /// Defaults to 1432 bytes for UDP, and 8192 bytes for Unix domain sockets .
150
179
///
151
180
/// # Errors
152
181
///
@@ -155,7 +184,7 @@ impl DogStatsDBuilder {
155
184
mut self ,
156
185
max_payload_len : usize ,
157
186
) -> Result < Self , BuildError > {
158
- self . max_payload_len = max_payload_len;
187
+ self . max_payload_len = Some ( max_payload_len) ;
159
188
self . validate_max_payload_len ( ) ?;
160
189
161
190
Ok ( self )
@@ -193,10 +222,10 @@ impl DogStatsDBuilder {
193
222
/// aggregation. A shorter interval will provide more frequent updates to the remote server, but will result in more
194
223
/// network traffic and processing overhead.
195
224
///
196
- /// Defaults to 3 seconds.
225
+ /// Defaults to 3 seconds in conservative mode, and 10 seconds in aggressive mode .
197
226
#[ must_use]
198
227
pub fn with_flush_interval ( mut self , flush_interval : Duration ) -> Self {
199
- self . flush_interval = flush_interval;
228
+ self . flush_interval = Some ( flush_interval) ;
200
229
self
201
230
}
202
231
@@ -276,25 +305,45 @@ impl DogStatsDBuilder {
276
305
pub fn build ( self ) -> Result < DogStatsDRecorder , BuildError > {
277
306
self . validate_max_payload_len ( ) ?;
278
307
308
+ let max_payload_len = self . get_max_payload_len ( ) ;
309
+ let flush_interval = self . get_flush_interval ( ) ;
310
+
311
+ debug ! (
312
+ agg_mode = %self . agg_mode,
313
+ histogram_sampling = self . histogram_sampling,
314
+ histogram_reservoir_size = self . histogram_reservoir_size,
315
+ histograms_as_distributions = self . histograms_as_distributions,
316
+ "Building DogStatsD exporter."
317
+ ) ;
279
318
let state_config = StateConfiguration {
280
319
agg_mode : self . agg_mode ,
281
320
telemetry : self . telemetry ,
282
321
histogram_sampling : self . histogram_sampling ,
283
322
histogram_reservoir_size : self . histogram_reservoir_size ,
284
323
histograms_as_distributions : self . histograms_as_distributions ,
285
324
} ;
325
+
286
326
let state = Arc :: new ( State :: new ( state_config) ) ;
287
327
288
328
let recorder = DogStatsDRecorder :: new ( Arc :: clone ( & state) ) ;
289
329
330
+ debug ! (
331
+ remote_addr = %self . remote_addr,
332
+ max_payload_len,
333
+ ?flush_interval,
334
+ write_timeout = ?self . write_timeout,
335
+ "Building DogStatsD forwarder."
336
+ ) ;
290
337
let forwarder_config = ForwarderConfiguration {
291
338
remote_addr : self . remote_addr ,
292
- max_payload_len : self . max_payload_len ,
293
- flush_interval : self . flush_interval ,
339
+ max_payload_len,
340
+ flush_interval,
294
341
write_timeout : self . write_timeout ,
295
342
} ;
296
343
297
344
if self . synchronous {
345
+ debug ! ( "Spawning synchronous forwarder backend." ) ;
346
+
298
347
let forwarder = forwarder:: sync:: Forwarder :: new ( forwarder_config, state) ;
299
348
300
349
std:: thread:: Builder :: new ( )
@@ -330,8 +379,8 @@ impl Default for DogStatsDBuilder {
330
379
DogStatsDBuilder {
331
380
remote_addr : RemoteAddr :: Udp ( vec ! [ SocketAddr :: from( ( [ 127 , 0 , 0 , 1 ] , 8125 ) ) ] ) ,
332
381
write_timeout : DEFAULT_WRITE_TIMEOUT ,
333
- max_payload_len : DEFAULT_MAX_PAYLOAD_LEN ,
334
- flush_interval : DEFAULT_FLUSH_INTERVAL ,
382
+ max_payload_len : None ,
383
+ flush_interval : None ,
335
384
synchronous : true ,
336
385
agg_mode : AggregationMode :: Conservative ,
337
386
telemetry : true ,
@@ -346,6 +395,31 @@ impl Default for DogStatsDBuilder {
346
395
mod tests {
347
396
use super :: * ;
348
397
398
+ #[ test]
399
+ fn default_flush_interval_agg_mode ( ) {
400
+ let builder =
401
+ DogStatsDBuilder :: default ( ) . with_aggregation_mode ( AggregationMode :: Conservative ) ;
402
+ assert_eq ! ( builder. get_flush_interval( ) , DEFAULT_FLUSH_INTERVAL_CONSERVATIVE ) ;
403
+
404
+ let builder =
405
+ DogStatsDBuilder :: default ( ) . with_aggregation_mode ( AggregationMode :: Aggressive ) ;
406
+ assert_eq ! ( builder. get_flush_interval( ) , DEFAULT_FLUSH_INTERVAL_AGGRESSIVE ) ;
407
+
408
+ let custom_flush_interval = Duration :: from_millis ( 123456789 ) ;
409
+ let builder = DogStatsDBuilder :: default ( ) . with_flush_interval ( custom_flush_interval) ;
410
+ assert_eq ! ( builder. get_flush_interval( ) , custom_flush_interval) ;
411
+ }
412
+
413
+ #[ test]
414
+ fn default_max_payload_len_udp ( ) {
415
+ let builder = DogStatsDBuilder :: default ( )
416
+ . with_remote_address ( "127.0.0.1:9999" )
417
+ . expect ( "address should be valid" ) ;
418
+
419
+ assert_eq ! ( builder. get_max_payload_len( ) , 1432 ) ;
420
+ assert ! ( builder. build( ) . is_ok( ) ) ;
421
+ }
422
+
349
423
#[ test]
350
424
fn max_payload_len_exceeds_udp_max_len ( ) {
351
425
let builder =
@@ -367,6 +441,23 @@ mod tests {
367
441
mod linux {
368
442
use super :: * ;
369
443
444
+ #[ test]
445
+ fn default_max_payload_len_uds ( ) {
446
+ let builder = DogStatsDBuilder :: default ( )
447
+ . with_remote_address ( "unix:///tmp/dogstatsd.sock" )
448
+ . expect ( "address should be valid" ) ;
449
+
450
+ assert_eq ! ( builder. get_max_payload_len( ) , 8192 ) ;
451
+ assert ! ( builder. build( ) . is_ok( ) ) ;
452
+
453
+ let builder = DogStatsDBuilder :: default ( )
454
+ . with_remote_address ( "unixgram:///tmp/dogstatsd.sock" )
455
+ . expect ( "address should be valid" ) ;
456
+
457
+ assert_eq ! ( builder. get_max_payload_len( ) , 8192 ) ;
458
+ assert ! ( builder. build( ) . is_ok( ) ) ;
459
+ }
460
+
370
461
#[ test]
371
462
fn max_payload_len_exceeds_udp_max_len_transport_change ( ) {
372
463
let builder = DogStatsDBuilder :: default ( )
0 commit comments