Skip to content

Commit d666794

Browse files
author
Simone Sanfratello
authored
feat: collect process metrics (#12)
* feat: event loop utilization in telemetry * release: v0.5.0
1 parent 0a191eb commit d666794

File tree

5 files changed

+147
-38
lines changed

5 files changed

+147
-38
lines changed

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ telemetry.resetDurations()
4444
telemetry.resetCounters()
4545
```
4646

47+
#### Options
48+
49+
- `configFile`: path to yml config file, see [Configuration file format](#configuration-file-format)
50+
4751
#### Telemetry instance methods
4852
* export: Export the metrics in `prometheus` format
4953
```
@@ -52,11 +56,14 @@ telemetry.resetCounters()
5256
counter_label_count_total{id="123"} 1 now
5357
counter_label_count_total{id="456"} 2 now
5458
```
59+
* get(name): Get a single metric
60+
* getGaugeValue(name): Get the value of the gauge metric
61+
* getHistogramValue(name): Get the value (sum) of the histogram metric
5562
* resetAll(): Reset all metrics
5663
* resetCounters(): Reset count and labelCount metrics
5764
* resetDurations(): Reset durations metrics
5865
* resetGauges(): Reset gauges metrics
59-
* async export(): Export values in Prometheus format
66+
* async export(): Export values in Prometheus format
6067
* increaseCount(category, amount = 1): Increase the count for a category
6168
* category: String - The given name of the category
6269
* increaseLabelCount(category, labels: Array, amount = 1): Increase the count for a key in a category
@@ -76,9 +83,10 @@ telemetry.resetCounters()
7683
* category: String - The given name of the category
7784
* promise: Promise - The function to be tracked
7885
79-
#### Configuration
86+
#### Configuration file format
87+
88+
Eg. of `metrics.yml`, to be passed to `configFile`
8089
81-
Eg. of `metrics.yml`
8290
```yaml
8391
---
8492
component: bitswap-peer
@@ -102,6 +110,13 @@ metrics:
102110
description: BitSwap Event Loop Utilization
103111
bitswap-total-connections:
104112
description: BitSwap Total Connections
113+
114+
process:
115+
elu:
116+
name: bitswap-elu
117+
description: Bitswap Event Loop Utilization
118+
interval: 500
119+
105120
version: 0.1.0
106121
buildDate: "20220307.1423"
107122
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "e-ipfs-core-lib",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"description": "E-IPFS core library",
55
"license": "(Apache-2.0 AND MIT)",
66
"homepage": "https://github.com/elastic-ipfs/core-lib",

src/telemetry.js

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
2+
import { performance } from 'node:perf_hooks'
13
import { readFileSync } from 'fs'
24
import { load as ymlLoad } from 'js-yaml'
35
import promClient from 'prom-client'
4-
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]
6+
const PERCENTILES = [0.1, 1, 10, 25, 50, 75, 90, 97.5, 99]
57

68
class Telemetry {
79
constructor ({ configFile, logger }) {
@@ -25,40 +27,46 @@ class Telemetry {
2527
// Setup
2628
this.component = component
2729
this.version = `${version}-build.${buildDate}`
30+
this.metrics = new Map()
2831

2932
// Create metrics
3033
for (const [category, metric] of Object.entries(metrics.count || {})) {
31-
/* eslint-disable-next-line no-new */
32-
new promClient.Counter({
34+
this.metrics.set(category, new promClient.Counter({
3335
name: category.replaceAll('-', '_'),
3436
help: metric.description,
3537
registers: [this.countRegistry, this.allRegistry] // specify a non-default registry
36-
})
38+
}))
3739
}
3840
for (const [category, metric] of Object.entries(metrics.labelCount || {})) {
39-
/* eslint-disable-next-line no-new */
40-
new promClient.Counter({
41+
this.metrics.set(category, new promClient.Counter({
4142
name: category.replaceAll('-', '_'),
4243
help: metric.description,
4344
labelNames: metric.labels,
4445
registers: [this.labelCountRegistry, this.allRegistry] // specify a non-default registry
45-
})
46+
}))
4647
}
4748
for (const [category, metric] of Object.entries(metrics.durations || {})) {
48-
/* eslint-disable-next-line no-new */
49-
new promClient.Histogram({
49+
this.metrics.set(category, new promClient.Histogram({
5050
name: category.replaceAll('-', '_'),
5151
help: metric.description,
5252
buckets: PERCENTILES,
5353
registers: [this.durationsRegistry, this.allRegistry] // specify a non-default registry
54-
})
54+
}))
5555
}
5656
for (const [category, metric] of Object.entries(metrics.gauge || {})) {
57-
/* eslint-disable-next-line no-new */
58-
new promClient.Gauge({
57+
this.metrics.set(category, new promClient.Gauge({
5958
name: category.replaceAll('-', '_'),
6059
help: metric.description,
6160
registers: [this.gaugeRegistry, this.allRegistry] // specify a non-default registry
61+
}))
62+
}
63+
64+
if (metrics.process?.elu) {
65+
this.collectEventLoopUtilization({
66+
name: metrics.process.elu.name,
67+
description: metrics.process.elu.description,
68+
interval: metrics.process.elu.interval,
69+
registers: [this.allRegistry]
6270
})
6371
}
6472
} catch (err) {
@@ -67,6 +75,23 @@ class Telemetry {
6775
}
6876
}
6977

78+
collectEventLoopUtilization ({ name, description, interval, registers }) {
79+
const metric = new promClient.Gauge({
80+
name: name.replaceAll('-', '_'),
81+
help: description,
82+
registers
83+
})
84+
this.metrics.set(name, metric)
85+
86+
let elu1 = performance.eventLoopUtilization()
87+
this.collectEluInterval = setInterval(() => {
88+
const elu2 = performance.eventLoopUtilization()
89+
const u = performance.eventLoopUtilization(elu2, elu1)
90+
metric.set(u.utilization)
91+
elu1 = elu2
92+
}, interval).unref()
93+
}
94+
7095
resetAll () {
7196
this.allRegistry.resetMetrics()
7297
}
@@ -88,38 +113,54 @@ class Telemetry {
88113
return this.allRegistry.metrics()
89114
}
90115

116+
get (name) {
117+
return this.metrics.get(name)
118+
}
119+
120+
getGaugeValue (name) {
121+
const value = this.get(name)
122+
if (!value) { return }
123+
return value.hashMap[''].value
124+
}
125+
126+
getHistogramValue (name) {
127+
const value = this.get(name)
128+
if (!value) { return }
129+
return value.hashMap && value.hashMap['']?.sum
130+
}
131+
91132
increaseCount (category, amount = 1) {
92-
const metric = this.countRegistry.getSingleMetric(category.replaceAll('-', '_'))
133+
const metric = this.metrics.get(category)
93134
if (!metric) throw new Error(`Metric ${category} not found`)
94135
return metric.inc(amount)
95136
}
96137

97138
increaseLabelCount (category, labels = [], amount = 1) {
98-
const metric = this.labelCountRegistry.getSingleMetric(category.replaceAll('-', '_'))
139+
const metric = this.metrics.get(category)
99140
if (!metric) throw new Error(`Metric ${category} not found`)
100141
return metric.labels(...labels).inc(amount)
101142
}
102143

103144
increaseGauge (category, amount = 1) {
104-
const metric = this.gaugeRegistry.getSingleMetric(category.replaceAll('-', '_'))
145+
const metric = this.metrics.get(category)
105146
if (!metric) throw new Error(`Metric ${category} not found`)
106147
return metric.inc(amount)
107148
}
108149

109150
decreaseGauge (category, amount = 1) {
110-
const metric = this.gaugeRegistry.getSingleMetric(category.replaceAll('-', '_'))
151+
const metric = this.metrics.get(category)
111152
if (!metric) throw new Error(`Metric ${category} not found`)
112153
return metric.inc(-1 * amount)
113154
}
114155

115156
setGauge (category, value) {
116-
const metric = this.gaugeRegistry.getSingleMetric(category.replaceAll('-', '_'))
157+
const metric = this.metrics.get(category)
117158
if (!metric) throw new Error(`Metric ${category} not found`)
118159
return metric.set(value)
119160
}
120161

121162
async trackDuration (category, promise) {
122-
const metric = this.durationsRegistry.getSingleMetric(category.replaceAll('-', '_'))
163+
const metric = this.metrics.get(category)
123164
if (!metric) throw new Error(`Metric ${category} not found`)
124165

125166
const end = metric.startTimer()

test/fixtures/process-metrics.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
component: bitswap-peer
3+
metrics:
4+
count:
5+
s3-request-count:
6+
description: AWS S3 requests
7+
8+
process:
9+
elu:
10+
name: bitswap-elu
11+
description: Bitswap Event Loop Utilization
12+
interval: 100
13+
14+
version: 0.2.0
15+
buildDate: "20220307.1423"

0 commit comments

Comments
 (0)