@@ -6,6 +6,7 @@ const dgram = require('dgram')
6
6
const isIP = require ( 'net' ) . isIP
7
7
const log = require ( './log' )
8
8
const { URL , format } = require ( 'url' )
9
+ const Histogram = require ( './histogram' )
9
10
10
11
const MAX_BUFFER_SIZE = 1024 // limit from the agent
11
12
@@ -193,6 +194,117 @@ class DogStatsDClient {
193
194
}
194
195
}
195
196
197
+ // TODO: Handle arrays of tags and tags translation.
198
+ class MetricsAggregationClient {
199
+ constructor ( client ) {
200
+ this . _client = client
201
+
202
+ this . reset ( )
203
+ }
204
+
205
+ flush ( ) {
206
+ this . _captureCounters ( )
207
+ this . _captureGauges ( )
208
+ this . _captureHistograms ( )
209
+
210
+ this . _client . flush ( )
211
+ }
212
+
213
+ reset ( ) {
214
+ this . _counters = { }
215
+ this . _gauges = { }
216
+ this . _histograms = { }
217
+ }
218
+
219
+ distribution ( name , value , tag ) {
220
+ this . _client . distribution ( name , value , tag && [ tag ] )
221
+ }
222
+
223
+ boolean ( name , value , tag ) {
224
+ this . gauge ( name , value ? 1 : 0 , tag )
225
+ }
226
+
227
+ histogram ( name , value , tag ) {
228
+ this . _histograms [ name ] = this . _histograms [ name ] || new Map ( )
229
+
230
+ if ( ! this . _histograms [ name ] . has ( tag ) ) {
231
+ this . _histograms [ name ] . set ( tag , new Histogram ( ) )
232
+ }
233
+
234
+ this . _histograms [ name ] . get ( tag ) . record ( value )
235
+ }
236
+
237
+ count ( name , count , tag , monotonic = false ) {
238
+ if ( typeof tag === 'boolean' ) {
239
+ monotonic = tag
240
+ tag = undefined
241
+ }
242
+
243
+ const map = monotonic ? this . _counters : this . _gauges
244
+
245
+ map [ name ] = map [ name ] || new Map ( )
246
+
247
+ const value = map [ name ] . get ( tag ) || 0
248
+
249
+ map [ name ] . set ( tag , value + count )
250
+ }
251
+
252
+ gauge ( name , value , tag ) {
253
+ this . _gauges [ name ] = this . _gauges [ name ] || new Map ( )
254
+ this . _gauges [ name ] . set ( tag , value )
255
+ }
256
+
257
+ increment ( name , count = 1 , tag , monotonic ) {
258
+ this . count ( name , count , tag , monotonic )
259
+ }
260
+
261
+ decrement ( name , count = 1 , tag ) {
262
+ this . count ( name , - count , tag )
263
+ }
264
+
265
+ _captureGauges ( ) {
266
+ Object . keys ( this . _gauges ) . forEach ( name => {
267
+ this . _gauges [ name ] . forEach ( ( value , tag ) => {
268
+ this . _client . gauge ( name , value , tag && [ tag ] )
269
+ } )
270
+ } )
271
+ }
272
+
273
+ _captureCounters ( ) {
274
+ Object . keys ( this . _counters ) . forEach ( name => {
275
+ this . _counters [ name ] . forEach ( ( value , tag ) => {
276
+ this . _client . increment ( name , value , tag && [ tag ] )
277
+ } )
278
+ } )
279
+
280
+ this . _counters = { }
281
+ }
282
+
283
+ _captureHistograms ( ) {
284
+ Object . keys ( this . _histograms ) . forEach ( name => {
285
+ this . _histograms [ name ] . forEach ( ( stats , tag ) => {
286
+ const tags = tag && [ tag ]
287
+
288
+ // Stats can contain garbage data when a value was never recorded.
289
+ if ( stats . count === 0 ) {
290
+ stats = { max : 0 , min : 0 , sum : 0 , avg : 0 , median : 0 , p95 : 0 , count : 0 }
291
+ }
292
+
293
+ this . _client . gauge ( `${ name } .min` , stats . min , tags )
294
+ this . _client . gauge ( `${ name } .max` , stats . max , tags )
295
+ this . _client . increment ( `${ name } .sum` , stats . sum , tags )
296
+ this . _client . increment ( `${ name } .total` , stats . sum , tags )
297
+ this . _client . gauge ( `${ name } .avg` , stats . avg , tags )
298
+ this . _client . increment ( `${ name } .count` , stats . count , tags )
299
+ this . _client . gauge ( `${ name } .median` , stats . median , tags )
300
+ this . _client . gauge ( `${ name } .95percentile` , stats . p95 , tags )
301
+
302
+ stats . reset ( )
303
+ } )
304
+ } )
305
+ }
306
+ }
307
+
196
308
/**
197
309
* This is a simplified user-facing proxy to the underlying DogStatsDClient instance
198
310
*
@@ -201,7 +313,7 @@ class DogStatsDClient {
201
313
class CustomMetrics {
202
314
constructor ( config ) {
203
315
const clientConfig = DogStatsDClient . generateClientConfig ( config )
204
- this . dogstatsd = new DogStatsDClient ( clientConfig )
316
+ this . _client = new MetricsAggregationClient ( new DogStatsDClient ( clientConfig ) )
205
317
206
318
const flush = this . flush . bind ( this )
207
319
@@ -212,47 +324,43 @@ class CustomMetrics {
212
324
}
213
325
214
326
increment ( stat , value = 1 , tags ) {
215
- return this . dogstatsd . increment (
216
- stat ,
217
- value ,
218
- CustomMetrics . tagTranslator ( tags )
219
- )
327
+ for ( const tag of this . _normalizeTags ( tags ) ) {
328
+ this . _client . increment ( stat , value , tag )
329
+ }
220
330
}
221
331
222
332
decrement ( stat , value = 1 , tags ) {
223
- return this . dogstatsd . decrement (
224
- stat ,
225
- value ,
226
- CustomMetrics . tagTranslator ( tags )
227
- )
333
+ for ( const tag of this . _normalizeTags ( tags ) ) {
334
+ this . _client . decrement ( stat , value , tag )
335
+ }
228
336
}
229
337
230
338
gauge ( stat , value , tags ) {
231
- return this . dogstatsd . gauge (
232
- stat ,
233
- value ,
234
- CustomMetrics . tagTranslator ( tags )
235
- )
339
+ for ( const tag of this . _normalizeTags ( tags ) ) {
340
+ this . _client . gauge ( stat , value , tag )
341
+ }
236
342
}
237
343
238
344
distribution ( stat , value , tags ) {
239
- return this . dogstatsd . distribution (
240
- stat ,
241
- value ,
242
- CustomMetrics . tagTranslator ( tags )
243
- )
345
+ for ( const tag of this . _normalizeTags ( tags ) ) {
346
+ this . _client . distribution ( stat , value , tag )
347
+ }
244
348
}
245
349
246
350
histogram ( stat , value , tags ) {
247
- return this . dogstatsd . histogram (
248
- stat ,
249
- value ,
250
- CustomMetrics . tagTranslator ( tags )
251
- )
351
+ for ( const tag of this . _normalizeTags ( tags ) ) {
352
+ this . _client . histogram ( stat , value , tag )
353
+ }
252
354
}
253
355
254
356
flush ( ) {
255
- return this . dogstatsd . flush ( )
357
+ return this . _client . flush ( )
358
+ }
359
+
360
+ _normalizeTags ( tags ) {
361
+ tags = CustomMetrics . tagTranslator ( tags )
362
+
363
+ return tags . length === 0 ? [ undefined ] : tags
256
364
}
257
365
258
366
/**
@@ -274,5 +382,6 @@ class CustomMetrics {
274
382
275
383
module . exports = {
276
384
DogStatsDClient,
277
- CustomMetrics
385
+ CustomMetrics,
386
+ MetricsAggregationClient
278
387
}
0 commit comments