-
Notifications
You must be signed in to change notification settings - Fork 230
/
Copy pathreporter.js
114 lines (95 loc) · 3.29 KB
/
reporter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
'use strict'
const afterAll = require('after-all-results')
const { Reporter } = require('measured-reporting')
const ObjectIdentityMap = require('object-identity-map')
class MetricsReporter extends Reporter {
constructor (agent, options = {}) {
super(options)
this.enabled = options.enabled
this._agent = agent
if (!this.enabled) {
this.shutdown()
}
}
_reportMetrics (metrics) {
if (!this.enabled) return
const baseDimensions = {
timestamp: Date.now() * 1000,
tags: this._getDimensions(metrics)
}
const next = afterAll(() => {
const seen = new ObjectIdentityMap()
for (const metric of metrics) {
// Due to limitations in measured-reporting, metrics dropped
// due to `metricsLimit` leave empty slots in the list.
if (!metric) continue
if (this.isStaleMetric(metric)) {
this.removeMetricFromRegistry(metric, this._registry)
continue
}
const data = seen.ensure(metric.dimensions, () => {
const metricData = unflattenBreakdown(metric.dimensions)
const merged = Object.assign({ samples: {} }, baseDimensions, metricData)
Object.assign(merged.tags, baseDimensions.tags, metricData.tags)
return merged
})
data.samples[metric.name] = {
value: metric.metricImpl.toJSON()
}
if (metric.metricImpl.constructor.name === 'Counter') {
// We need a way to avoid ignoring metrics that might go stale
// because we want values for each counter to appear in every document
// This is not a great check but there does not seem to be a way
// to provide meta data about a metric to enable to better check
if (!metric.name.startsWith('kibana')) {
metric.metricImpl.reset()
}
}
}
if (this._agent._transport) {
for (const metric of seen.values()) {
this._agent._transport.sendMetricSet(metric)
}
}
})
for (const collector of this._registry.collectors) {
collector.collect(next())
}
}
isStaleMetric (metric) {
// if a metric is a counting metric and that count is
// zero, then the metric is considered stale
if (metric.metricImpl && metric.metricImpl._count === 0) {
// We need a way to avoid ignoring metrics that might go stale
// because we want values for each counter to appear in every document
// This is not a great check but there does not seem to be a way
// to provide meta data about a metric to enable to better check
if (metric.name.startsWith('kibana')) return false
return true
}
return false
}
removeMetricFromRegistry (metric) {
if (!this._registry || !this._registry._metrics) {
return
}
const key = this._registry._generateStorageKey(metric.name, metric.dimensions)
return this._registry._metrics.delete(key)
}
}
module.exports = MetricsReporter
function unflattenBreakdown (source) {
const target = {
tags: {}
}
for (const [key, value] of Object.entries(source)) {
if (key.includes('::')) {
const [parent, child] = key.split('::')
if (!target[parent]) target[parent] = {}
target[parent][child] = value
} else {
target.tags[key] = value
}
}
return target
}