1
-
2
1
import { readFileSync } from 'fs'
3
2
import { load as ymlLoad } from 'js-yaml'
4
3
import * as hdr from 'hdr-histogram-js'
5
4
6
5
const PERCENTILES = [ 0.001 , 0.01 , 0.1 , 1 , 2.5 , 10 , 25 , 50 , 75 , 90 , 97.5 , 99 , 99.9 , 99.99 , 99.999 ]
7
6
7
+ const METRIC_GROUPED_COUNT = 'grouped-count'
8
+ const METRIC_DURATIONS = 'durations'
9
+ const METRIC_COUNT = 'count'
10
+
11
+ const TYPE_COUNTER = 'counter'
12
+ const TYPE_HISTOGRAM = 'histogram'
13
+ const TYPE_GAUGE = 'gauge'
14
+
8
15
class Aggregator {
9
16
constructor ( category , description , metric , type ) {
10
17
this . tag = `${ category } -${ metric } `
@@ -14,16 +21,18 @@ class Aggregator {
14
21
// type is optional
15
22
if ( ! type ) {
16
23
// set the type by the metric
17
- if ( metric === 'durations' ) {
18
- this . type = 'histogram'
24
+ if ( metric === METRIC_DURATIONS ) {
25
+ this . type = TYPE_HISTOGRAM
19
26
} else {
20
- this . type = 'counter'
27
+ this . type = TYPE_COUNTER
21
28
this . exportName += '_total'
29
+ this . isGrouped = metric === METRIC_GROUPED_COUNT
22
30
}
23
31
} else {
24
32
this . type = type
25
33
}
26
34
35
+ this . groupedSum = { }
27
36
this . sum = 0
28
37
hdr . initWebAssemblySync ( )
29
38
this . histogram = hdr . build ( {
@@ -37,22 +46,35 @@ class Aggregator {
37
46
record ( value ) {
38
47
this . sum += value
39
48
40
- if ( this . type === 'histogram' ) {
49
+ if ( this . type === TYPE_HISTOGRAM ) {
41
50
this . histogram . recordValue ( value )
42
51
}
43
52
}
44
53
54
+ recordWithKey ( key , value ) {
55
+ if ( ! this . groupedSum [ key ] ) {
56
+ this . groupedSum [ key ] = value
57
+ } else {
58
+ this . groupedSum [ key ] += value
59
+ }
60
+ }
61
+
45
62
reset ( ) {
46
63
this . sum = 0
64
+ this . groupedSum = { }
47
65
this . histogram . reset ( )
48
66
}
49
67
50
68
current ( ) {
51
69
const { minNonZeroValue : min , maxValue : max , mean, stdDeviation : stdDev , totalCount : count } = this . histogram
52
70
53
71
const value = {
54
- empty : ( this . type === 'histogram' && count === 0 ) || ( this . type === 'counter' && this . sum === 0 ) ,
72
+ empty : ( this . type === TYPE_HISTOGRAM && count === 0 ) ||
73
+ ( ( this . type === TYPE_COUNTER && ! this . isGrouped ) && this . sum === 0 ) ||
74
+ ( ( this . type === TYPE_COUNTER && this . isGrouped ) && Object . keys ( this . groupedSum ) . length === 0 ) ,
55
75
sum : this . sum ,
76
+ isGrouped : this . isGrouped ,
77
+ groupedSum : this . groupedSum ,
56
78
histogram :
57
79
count > 0
58
80
? {
@@ -96,8 +118,9 @@ class Telemetry {
96
118
// Create metrics
97
119
this . metrics = new Map ( )
98
120
for ( const [ category , description ] of Object . entries ( metrics ) ) {
99
- this . createMetric ( category , description , 'count' )
100
- this . createMetric ( category , description , 'durations' )
121
+ this . createMetric ( category , description , METRIC_COUNT )
122
+ this . createMetric ( category , description , METRIC_GROUPED_COUNT )
123
+ this . createMetric ( category , description , METRIC_DURATIONS )
101
124
}
102
125
} catch ( err ) {
103
126
logger . error ( { err } , 'error in telemetry constructor' )
@@ -138,14 +161,18 @@ class Telemetry {
138
161
output += `# HELP ${ metric . exportName } ${ metric . description } \n`
139
162
output += `# TYPE ${ metric . exportName } ${ metric . type } \n`
140
163
141
- if ( metric . type === 'histogram' ) {
164
+ if ( metric . type === TYPE_HISTOGRAM ) {
142
165
output += `${ metric . exportName } _count ${ current . histogram . count } ${ current . timestamp } \n`
143
166
output += `${ metric . exportName } _sum ${ current . sum } ${ current . timestamp } \n`
144
167
145
168
const percentilesValues = current . histogram . percentiles
146
169
for ( const percentile of PERCENTILES ) {
147
170
output += `${ metric . exportName } _bucket{le="${ percentile } "} ${ percentilesValues [ percentile ] } ${ current . timestamp } \n`
148
171
}
172
+ } else if ( metric . type === TYPE_COUNTER && metric . isGrouped ) {
173
+ for ( const [ key , value ] of Object . entries ( current . groupedSum ) ) {
174
+ output += `${ metric . exportName } ${ key } ${ value } ${ current . timestamp } \n`
175
+ }
149
176
} else {
150
177
output += `${ metric . exportName } ${ current . sum } ${ current . timestamp } \n`
151
178
}
@@ -159,17 +186,27 @@ class Telemetry {
159
186
}
160
187
161
188
increaseCount ( category , amount = 1 ) {
162
- const metric = this . ensureMetric ( category , 'count' )
189
+ const metric = this . ensureMetric ( category , METRIC_COUNT )
163
190
metric . record ( amount )
164
191
}
165
192
166
193
decreaseCount ( category , amount = 1 ) {
167
- const metric = this . ensureMetric ( category , 'count' )
194
+ const metric = this . ensureMetric ( category , METRIC_COUNT )
168
195
metric . record ( - 1 * amount )
169
196
}
170
197
198
+ increaseCountWithKey ( category , key , amount = 1 ) {
199
+ const metric = this . ensureMetric ( category , METRIC_GROUPED_COUNT )
200
+ metric . recordWithKey ( key , amount )
201
+ }
202
+
203
+ decreaseCountWithKey ( category , key , amount = 1 ) {
204
+ const metric = this . ensureMetric ( category , METRIC_GROUPED_COUNT )
205
+ metric . recordWithKey ( key , - 1 * amount )
206
+ }
207
+
171
208
async trackDuration ( category , promise ) {
172
- const metric = this . ensureMetric ( category , 'durations' )
209
+ const metric = this . ensureMetric ( category , METRIC_DURATIONS )
173
210
const startTime = process . hrtime . bigint ( )
174
211
175
212
try {
@@ -180,4 +217,4 @@ class Telemetry {
180
217
}
181
218
}
182
219
183
- export { Telemetry }
220
+ export { Telemetry , METRIC_COUNT , METRIC_DURATIONS , METRIC_GROUPED_COUNT , TYPE_COUNTER , TYPE_HISTOGRAM , TYPE_GAUGE }
0 commit comments