@libp2p/opentelemetry-metrics 0.0.0-abe9bd154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/index.min.js +3 -0
- package/dist/src/counter-group.d.ts +12 -0
- package/dist/src/counter-group.d.ts.map +1 -0
- package/dist/src/counter-group.js +31 -0
- package/dist/src/counter-group.js.map +1 -0
- package/dist/src/counter.d.ts +9 -0
- package/dist/src/counter.d.ts.map +1 -0
- package/dist/src/counter.js +13 -0
- package/dist/src/counter.js.map +1 -0
- package/dist/src/histogram-group.d.ts +11 -0
- package/dist/src/histogram-group.d.ts.map +1 -0
- package/dist/src/histogram-group.js +27 -0
- package/dist/src/histogram-group.js.map +1 -0
- package/dist/src/histogram.d.ts +10 -0
- package/dist/src/histogram.d.ts.map +1 -0
- package/dist/src/histogram.js +19 -0
- package/dist/src/histogram.js.map +1 -0
- package/dist/src/index.d.ts +67 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +416 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/metric-group.d.ts +14 -0
- package/dist/src/metric-group.d.ts.map +1 -0
- package/dist/src/metric-group.js +58 -0
- package/dist/src/metric-group.js.map +1 -0
- package/dist/src/metric.d.ts +13 -0
- package/dist/src/metric.d.ts.map +1 -0
- package/dist/src/metric.js +35 -0
- package/dist/src/metric.js.map +1 -0
- package/dist/src/summary-group.d.ts +12 -0
- package/dist/src/summary-group.d.ts.map +1 -0
- package/dist/src/summary-group.js +36 -0
- package/dist/src/summary-group.js.map +1 -0
- package/dist/src/summary.d.ts +10 -0
- package/dist/src/summary.d.ts.map +1 -0
- package/dist/src/summary.js +19 -0
- package/dist/src/summary.js.map +1 -0
- package/dist/src/system-metrics.browser.d.ts +2 -0
- package/dist/src/system-metrics.browser.d.ts.map +1 -0
- package/dist/src/system-metrics.browser.js +3 -0
- package/dist/src/system-metrics.browser.js.map +1 -0
- package/dist/src/system-metrics.d.ts +6 -0
- package/dist/src/system-metrics.d.ts.map +1 -0
- package/dist/src/system-metrics.js +439 -0
- package/dist/src/system-metrics.js.map +1 -0
- package/package.json +62 -0
- package/src/counter-group.ts +38 -0
- package/src/counter.ts +18 -0
- package/src/histogram-group.ts +34 -0
- package/src/histogram.ts +26 -0
- package/src/index.ts +557 -0
- package/src/metric-group.ts +69 -0
- package/src/metric.ts +44 -0
- package/src/summary-group.ts +43 -0
- package/src/summary.ts +26 -0
- package/src/system-metrics.browser.ts +3 -0
- package/src/system-metrics.ts +504 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { HistogramGroup, StopTimer } from '@libp2p/interface'
|
|
2
|
+
import type { Gauge } from '@opentelemetry/api'
|
|
3
|
+
|
|
4
|
+
export class OpenTelemetrySummaryGroup implements HistogramGroup {
|
|
5
|
+
private readonly label: string
|
|
6
|
+
private readonly gauge: Gauge
|
|
7
|
+
private readonly lastValues: Record<string, number>
|
|
8
|
+
|
|
9
|
+
constructor (label: string, gauge: Gauge) {
|
|
10
|
+
this.label = label
|
|
11
|
+
this.gauge = gauge
|
|
12
|
+
this.lastValues = {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
observe (values: Record<string, number>): void {
|
|
16
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
17
|
+
this.lastValues[key] = value
|
|
18
|
+
this.gauge.record(value, {
|
|
19
|
+
[this.label]: key
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
reset (): void {
|
|
25
|
+
Object.keys(this.lastValues).forEach(key => {
|
|
26
|
+
this.lastValues[key] = 0
|
|
27
|
+
this.gauge.record(0, {
|
|
28
|
+
[this.label]: key
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
timer (key: string): StopTimer {
|
|
34
|
+
const start = Date.now()
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
this.lastValues[key] = Date.now() - start
|
|
38
|
+
this.gauge.record(this.lastValues[key], {
|
|
39
|
+
[this.label]: key
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/summary.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { StopTimer, Summary } from '@libp2p/interface'
|
|
2
|
+
import type { Gauge } from '@opentelemetry/api'
|
|
3
|
+
|
|
4
|
+
export class OpenTelemetrySummary implements Summary {
|
|
5
|
+
private readonly gauge: Gauge
|
|
6
|
+
|
|
7
|
+
constructor (gauge: Gauge) {
|
|
8
|
+
this.gauge = gauge
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
observe (value: number): void {
|
|
12
|
+
this.gauge.record(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
reset (): void {
|
|
16
|
+
this.gauge.record(0)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
timer (): StopTimer {
|
|
20
|
+
const start = Date.now()
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
this.observe(Date.now() - start)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { statfs } from 'node:fs/promises'
|
|
3
|
+
import { totalmem } from 'node:os'
|
|
4
|
+
import { monitorEventLoopDelay, PerformanceObserver, constants as PerfHooksConstants } from 'node:perf_hooks'
|
|
5
|
+
import { getHeapSpaceStatistics } from 'node:v8'
|
|
6
|
+
import type { Metrics } from '@libp2p/interface'
|
|
7
|
+
|
|
8
|
+
export interface SystemMetricsOptions {
|
|
9
|
+
statfsLocation?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function collectSystemMetrics (metrics: Metrics, init?: SystemMetricsOptions): void {
|
|
13
|
+
metrics.registerMetricGroup('nodejs_memory_usage_bytes', {
|
|
14
|
+
label: 'memory',
|
|
15
|
+
calculate: () => {
|
|
16
|
+
return {
|
|
17
|
+
...process.memoryUsage()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
const totalMemoryMetric = metrics.registerMetric('nodejs_memory_total_bytes')
|
|
22
|
+
totalMemoryMetric.update(totalmem())
|
|
23
|
+
|
|
24
|
+
metrics.registerMetricGroup('nodejs_fs_usage_bytes', {
|
|
25
|
+
label: 'filesystem',
|
|
26
|
+
calculate: async () => {
|
|
27
|
+
const stats = await statfs(init?.statfsLocation ?? process.cwd())
|
|
28
|
+
const total = stats.bsize * stats.blocks
|
|
29
|
+
const available = stats.bsize * stats.bavail
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
total,
|
|
33
|
+
free: stats.bsize * stats.bfree,
|
|
34
|
+
available,
|
|
35
|
+
used: (available / total) * 100
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
collectProcessCPUMetrics(metrics)
|
|
41
|
+
collectProcessStartTime(metrics)
|
|
42
|
+
collectMemoryHeap(metrics)
|
|
43
|
+
collectOpenFileDescriptors(metrics)
|
|
44
|
+
collectMaxFileDescriptors(metrics)
|
|
45
|
+
collectEventLoopStats(metrics)
|
|
46
|
+
collectProcessResources(metrics)
|
|
47
|
+
collectProcessHandles(metrics)
|
|
48
|
+
collectProcessRequests(metrics)
|
|
49
|
+
collectHeapSizeAndUsed(metrics)
|
|
50
|
+
collectHeapSpacesSizeAndUsed(metrics)
|
|
51
|
+
collectNodeVersion(metrics)
|
|
52
|
+
collectGcStats(metrics)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processCpuTotal.js
|
|
57
|
+
*/
|
|
58
|
+
function collectProcessCPUMetrics (metrics: Metrics): void {
|
|
59
|
+
let lastCpuUsage = process.cpuUsage()
|
|
60
|
+
const cpuUserSecondsTotal = metrics.registerCounter('process_cpu_user_seconds_total', {
|
|
61
|
+
help: 'Total user CPU time spent in seconds.'
|
|
62
|
+
})
|
|
63
|
+
const cpuSystemSecondsTotal = metrics.registerCounter('process_cpu_system_seconds_total', {
|
|
64
|
+
help: 'Total system CPU time spent in seconds.'
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
metrics.registerCounter('process_cpu_seconds_total', {
|
|
68
|
+
help: 'Total user and system CPU time spent in seconds.',
|
|
69
|
+
calculate: () => {
|
|
70
|
+
const cpuUsage = process.cpuUsage()
|
|
71
|
+
const userUsageMicros = cpuUsage.user - lastCpuUsage.user
|
|
72
|
+
const systemUsageMicros = cpuUsage.system - lastCpuUsage.system
|
|
73
|
+
lastCpuUsage = cpuUsage
|
|
74
|
+
|
|
75
|
+
cpuUserSecondsTotal.increment(userUsageMicros / 1e6)
|
|
76
|
+
cpuSystemSecondsTotal.increment(systemUsageMicros / 1e6)
|
|
77
|
+
|
|
78
|
+
return (userUsageMicros + systemUsageMicros) / 1e6
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processStartTime.js
|
|
85
|
+
*/
|
|
86
|
+
function collectProcessStartTime (metrics: Metrics): void {
|
|
87
|
+
const metric = metrics.registerMetric('process_start_time_seconds', {
|
|
88
|
+
help: 'Start time of the process since unix epoch in seconds.'
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
metric.update(Math.round(Date.now() / 1000 - process.uptime()))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/osMemoryHeap.js
|
|
96
|
+
*/
|
|
97
|
+
function collectMemoryHeap (metrics: Metrics): void {
|
|
98
|
+
metrics.registerMetric('process_resident_memory_bytes', {
|
|
99
|
+
help: 'Resident memory size in bytes.',
|
|
100
|
+
calculate: () => {
|
|
101
|
+
try {
|
|
102
|
+
return process.memoryUsage().rss
|
|
103
|
+
} catch {}
|
|
104
|
+
return 0
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
metrics.registerMetric('process_virtual_memory_bytes', {
|
|
108
|
+
help: 'Virtual memory size in bytes.',
|
|
109
|
+
calculate: () => {
|
|
110
|
+
// this involves doing sync io in prom-client so skip it
|
|
111
|
+
// https://github.com/siimon/prom-client/blob/c1d76c5d497ef803f6bd90c56c713c3fa811c3e0/lib/metrics/osMemoryHeapLinux.js#L53C5-L54C52
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
metrics.registerMetric('process_heap_bytes', {
|
|
116
|
+
help: 'Process heap size in bytes.',
|
|
117
|
+
calculate: () => {
|
|
118
|
+
try {
|
|
119
|
+
return process.memoryUsage().heapTotal
|
|
120
|
+
} catch {}
|
|
121
|
+
return 0
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processOpenFileDescriptors.js
|
|
128
|
+
*/
|
|
129
|
+
function collectOpenFileDescriptors (metrics: Metrics): void {
|
|
130
|
+
if (process.platform !== 'linux') {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
metrics.registerMetric('process_open_fds', {
|
|
135
|
+
help: 'Number of open file descriptors.',
|
|
136
|
+
calculate: () => {
|
|
137
|
+
try {
|
|
138
|
+
const fds = readdirSync('/proc/self/fd')
|
|
139
|
+
// Minus 1 to not count the fd that was used by readdirSync(),
|
|
140
|
+
// it's now closed.
|
|
141
|
+
return fds.length - 1
|
|
142
|
+
} catch {}
|
|
143
|
+
|
|
144
|
+
return 0
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processMaxFileDescriptors.js
|
|
151
|
+
*/
|
|
152
|
+
function collectMaxFileDescriptors (metrics: Metrics): void {
|
|
153
|
+
let maxFds: number | undefined
|
|
154
|
+
|
|
155
|
+
// This will fail if a linux-like procfs is not available.
|
|
156
|
+
try {
|
|
157
|
+
const limits = readFileSync('/proc/self/limits', 'utf8')
|
|
158
|
+
const lines = limits.split('\n')
|
|
159
|
+
for (const line of lines) {
|
|
160
|
+
if (line.startsWith('Max open files')) {
|
|
161
|
+
const parts = line.split(/ +/)
|
|
162
|
+
maxFds = Number(parts[1])
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (maxFds == null) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const metric = metrics.registerMetric('process_max_fds', {
|
|
175
|
+
help: 'Maximum number of open file descriptors.'
|
|
176
|
+
})
|
|
177
|
+
metric.update(maxFds)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/eventLoopLag.js
|
|
182
|
+
*/
|
|
183
|
+
function collectEventLoopStats (metrics: Metrics): void {
|
|
184
|
+
try {
|
|
185
|
+
const histogram = monitorEventLoopDelay()
|
|
186
|
+
histogram.enable()
|
|
187
|
+
|
|
188
|
+
metrics.registerMetric('nodejs_eventloop_lag_seconds', {
|
|
189
|
+
help: 'Lag of event loop in seconds.',
|
|
190
|
+
calculate: async () => {
|
|
191
|
+
const start = process.hrtime()
|
|
192
|
+
|
|
193
|
+
return new Promise<number>(resolve => {
|
|
194
|
+
setImmediate(() => {
|
|
195
|
+
const delta = process.hrtime(start)
|
|
196
|
+
const nanosec = delta[0] * 1e9 + delta[1]
|
|
197
|
+
const seconds = nanosec / 1e9
|
|
198
|
+
|
|
199
|
+
lagMin.update(histogram.min / 1e9)
|
|
200
|
+
lagMax.update(histogram.max / 1e9)
|
|
201
|
+
lagMean.update(histogram.mean / 1e9)
|
|
202
|
+
lagStddev.update(histogram.stddev / 1e9)
|
|
203
|
+
lagP50.update(histogram.percentile(50) / 1e9)
|
|
204
|
+
lagP90.update(histogram.percentile(90) / 1e9)
|
|
205
|
+
lagP99.update(histogram.percentile(99) / 1e9)
|
|
206
|
+
|
|
207
|
+
histogram.reset()
|
|
208
|
+
|
|
209
|
+
resolve(seconds)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
const lagMin = metrics.registerMetric('nodejs_eventloop_lag_min_seconds', {
|
|
215
|
+
help: 'The minimum recorded event loop delay.'
|
|
216
|
+
})
|
|
217
|
+
const lagMax = metrics.registerMetric('nodejs_eventloop_lag_max_seconds', {
|
|
218
|
+
help: 'The maximum recorded event loop delay.'
|
|
219
|
+
})
|
|
220
|
+
const lagMean = metrics.registerMetric('nodejs_eventloop_lag_mean_seconds', {
|
|
221
|
+
help: 'The mean of the recorded event loop delays.'
|
|
222
|
+
})
|
|
223
|
+
const lagStddev = metrics.registerMetric('nodejs_eventloop_lag_stddev_seconds', {
|
|
224
|
+
help: 'The standard deviation of the recorded event loop delays.'
|
|
225
|
+
})
|
|
226
|
+
const lagP50 = metrics.registerMetric('nodejs_eventloop_lag_p50_seconds', {
|
|
227
|
+
help: 'The 50th percentile of the recorded event loop delays.'
|
|
228
|
+
})
|
|
229
|
+
const lagP90 = metrics.registerMetric('nodejs_eventloop_lag_p90_seconds', {
|
|
230
|
+
help: 'The 90th percentile of the recorded event loop delays.'
|
|
231
|
+
})
|
|
232
|
+
const lagP99 = metrics.registerMetric('nodejs_eventloop_lag_p99_seconds', {
|
|
233
|
+
help: 'The 99th percentile of the recorded event loop delays.'
|
|
234
|
+
})
|
|
235
|
+
} catch (err: any) {
|
|
236
|
+
if (err.code === 'ERR_NOT_IMPLEMENTED') {
|
|
237
|
+
return // Bun
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
throw err
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processResources.js
|
|
246
|
+
*/
|
|
247
|
+
function collectProcessResources (metrics: Metrics): void {
|
|
248
|
+
// Don't do anything if the function does not exist in previous nodes (exists in node@17.3.0)
|
|
249
|
+
if (typeof process.getActiveResourcesInfo !== 'function') {
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
metrics.registerMetricGroup('nodejs_active_resources', {
|
|
254
|
+
help: 'Number of active resources that are currently keeping the event loop alive, grouped by async resource type.',
|
|
255
|
+
label: 'type',
|
|
256
|
+
calculate: () => {
|
|
257
|
+
const resources = process.getActiveResourcesInfo()
|
|
258
|
+
|
|
259
|
+
const data: Record<string, number> = {}
|
|
260
|
+
|
|
261
|
+
for (let i = 0; i < resources.length; i++) {
|
|
262
|
+
const resource = resources[i]
|
|
263
|
+
|
|
264
|
+
if (Object.hasOwn(data, resource)) {
|
|
265
|
+
data[resource] += 1
|
|
266
|
+
} else {
|
|
267
|
+
data[resource] = 1
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return data
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
metrics.registerMetric('nodejs_active_resources_total', {
|
|
276
|
+
help: 'Total number of active resources.',
|
|
277
|
+
calculate: () => {
|
|
278
|
+
const resources = process.getActiveResourcesInfo()
|
|
279
|
+
|
|
280
|
+
return resources.length
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processHandles.js
|
|
287
|
+
*/
|
|
288
|
+
function collectProcessHandles (metrics: Metrics): void {
|
|
289
|
+
// Don't do anything if the function is removed in later nodes (exists in node@6-12...)
|
|
290
|
+
// @ts-expect-error not part of the public API
|
|
291
|
+
if (typeof process._getActiveHandles !== 'function') {
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
metrics.registerMetricGroup('nodejs_active_handles', {
|
|
296
|
+
help: 'Number of active libuv handles grouped by handle type. Every handle type is C++ class name.',
|
|
297
|
+
label: 'type',
|
|
298
|
+
calculate: () => {
|
|
299
|
+
// @ts-expect-error not part of the public API
|
|
300
|
+
const resources = process._getActiveHandles()
|
|
301
|
+
|
|
302
|
+
const data: Record<string, number> = {}
|
|
303
|
+
|
|
304
|
+
for (let i = 0; i < resources.length; i++) {
|
|
305
|
+
const listElement = resources[i]
|
|
306
|
+
|
|
307
|
+
if (listElement == null || typeof listElement.constructor === 'undefined') {
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (Object.hasOwnProperty.call(data, listElement.constructor.name)) {
|
|
312
|
+
data[listElement.constructor.name] += 1
|
|
313
|
+
} else {
|
|
314
|
+
data[listElement.constructor.name] = 1
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return data
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
metrics.registerMetric('nodejs_active_handles_total', {
|
|
323
|
+
help: 'Total number of active handles.',
|
|
324
|
+
calculate: () => {
|
|
325
|
+
// @ts-expect-error not part of the public API
|
|
326
|
+
const resources = process._getActiveHandles()
|
|
327
|
+
|
|
328
|
+
return resources.length
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/processRequests.js
|
|
335
|
+
*/
|
|
336
|
+
function collectProcessRequests (metrics: Metrics): void {
|
|
337
|
+
// Don't do anything if the function is removed in later nodes (exists in node@6)
|
|
338
|
+
// @ts-expect-error not part of the public API
|
|
339
|
+
if (typeof process._getActiveRequests !== 'function') {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
metrics.registerMetricGroup('nodejs_active_requests', {
|
|
344
|
+
help: 'Number of active libuv requests grouped by request type. Every request type is C++ class name.',
|
|
345
|
+
label: 'type',
|
|
346
|
+
calculate: () => {
|
|
347
|
+
// @ts-expect-error not part of the public API
|
|
348
|
+
const resources = process._getActiveRequests()
|
|
349
|
+
|
|
350
|
+
const data: Record<string, number> = {}
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < resources.length; i++) {
|
|
353
|
+
const listElement = resources[i]
|
|
354
|
+
|
|
355
|
+
if (listElement == null || typeof listElement.constructor === 'undefined') {
|
|
356
|
+
continue
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (Object.hasOwnProperty.call(data, listElement.constructor.name)) {
|
|
360
|
+
data[listElement.constructor.name] += 1
|
|
361
|
+
} else {
|
|
362
|
+
data[listElement.constructor.name] = 1
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return data
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
metrics.registerMetric('nodejs_active_requests_total', {
|
|
371
|
+
help: 'Total number of active requests.',
|
|
372
|
+
calculate: () => {
|
|
373
|
+
// @ts-expect-error not part of the public API
|
|
374
|
+
const resources = process._getActiveRequests()
|
|
375
|
+
|
|
376
|
+
return resources.length
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/heapSizeAndUsed.js
|
|
383
|
+
*/
|
|
384
|
+
function collectHeapSizeAndUsed (metrics: Metrics): void {
|
|
385
|
+
const heapSizeUsed = metrics.registerMetric('nodejs_heap_size_used_bytes', {
|
|
386
|
+
help: 'Process heap size used from Node.js in bytes.'
|
|
387
|
+
})
|
|
388
|
+
const externalMemUsed = metrics.registerMetric('nodejs_external_memory_bytes', {
|
|
389
|
+
help: 'Node.js external memory size in bytes.'
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
metrics.registerMetric('nodejs_heap_size_total_bytes', {
|
|
393
|
+
help: 'Process heap size from Node.js in bytes.',
|
|
394
|
+
calculate: () => {
|
|
395
|
+
try {
|
|
396
|
+
const memUsage = process.memoryUsage()
|
|
397
|
+
|
|
398
|
+
heapSizeUsed.update(memUsage.heapUsed)
|
|
399
|
+
if (memUsage.external !== undefined) {
|
|
400
|
+
externalMemUsed.update(memUsage.external)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return memUsage.heapTotal
|
|
404
|
+
} catch {}
|
|
405
|
+
|
|
406
|
+
return 0
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/heapSpacesSizeAndUsed.js
|
|
413
|
+
*/
|
|
414
|
+
function collectHeapSpacesSizeAndUsed (metrics: Metrics): void {
|
|
415
|
+
try {
|
|
416
|
+
getHeapSpaceStatistics()
|
|
417
|
+
} catch (err: any) {
|
|
418
|
+
if (err.code === 'ERR_NOT_IMPLEMENTED') {
|
|
419
|
+
return // Bun
|
|
420
|
+
}
|
|
421
|
+
throw err
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const used = metrics.registerMetricGroup('nodejs_heap_space_size_used_bytes', {
|
|
425
|
+
help: 'Process heap space size used from Node.js in bytes.',
|
|
426
|
+
label: 'space'
|
|
427
|
+
})
|
|
428
|
+
const available = metrics.registerMetricGroup('nodejs_heap_space_size_available_bytes', {
|
|
429
|
+
help: 'Process heap space size available from Node.js in bytes.',
|
|
430
|
+
label: 'space'
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
metrics.registerMetricGroup('nodejs_heap_space_size_total_bytes', {
|
|
434
|
+
help: 'Process heap space size total from Node.js in bytes.',
|
|
435
|
+
label: 'space',
|
|
436
|
+
calculate: () => {
|
|
437
|
+
const data: Record<string, number> = {}
|
|
438
|
+
|
|
439
|
+
for (const space of getHeapSpaceStatistics()) {
|
|
440
|
+
const spaceName = space.space_name.substr(0, space.space_name.indexOf('_space'))
|
|
441
|
+
|
|
442
|
+
used.update({
|
|
443
|
+
[spaceName]: space.space_used_size
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
available.update({
|
|
447
|
+
[spaceName]: space.space_available_size
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
data[spaceName] = space.space_size
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return data
|
|
454
|
+
}
|
|
455
|
+
})
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/version.js
|
|
460
|
+
*/
|
|
461
|
+
function collectNodeVersion (metrics: Metrics): void {
|
|
462
|
+
const metric = metrics.registerMetricGroup('nodejs_version_info', {
|
|
463
|
+
help: 'Node.js version info.'
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
const version = process.version
|
|
467
|
+
const versionSegments = version.slice(1).split('.').map(Number)
|
|
468
|
+
|
|
469
|
+
// @ts-expect-error use internal API to get same result as prom-client
|
|
470
|
+
metric.gauge.record(1, {
|
|
471
|
+
version,
|
|
472
|
+
major: versionSegments[0],
|
|
473
|
+
minor: versionSegments[1],
|
|
474
|
+
patch: versionSegments[2]
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @see https://github.com/siimon/prom-client/blob/master/lib/metrics/gc.js
|
|
480
|
+
*/
|
|
481
|
+
function collectGcStats (metrics: Metrics): void {
|
|
482
|
+
const histogram = metrics.registerHistogramGroup('nodejs_gc_duration_seconds_bucket', {
|
|
483
|
+
buckets: [0.001, 0.01, 0.1, 1, 2, 5],
|
|
484
|
+
label: 'kind'
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
const kinds: string[] = []
|
|
488
|
+
kinds[PerfHooksConstants.NODE_PERFORMANCE_GC_MAJOR] = 'major'
|
|
489
|
+
kinds[PerfHooksConstants.NODE_PERFORMANCE_GC_MINOR] = 'minor'
|
|
490
|
+
kinds[PerfHooksConstants.NODE_PERFORMANCE_GC_INCREMENTAL] = 'incremental'
|
|
491
|
+
kinds[PerfHooksConstants.NODE_PERFORMANCE_GC_WEAKCB] = 'weakcb'
|
|
492
|
+
|
|
493
|
+
const obs = new PerformanceObserver(list => {
|
|
494
|
+
const entry = list.getEntries()[0]
|
|
495
|
+
// @ts-expect-error types are incomplete
|
|
496
|
+
const kind = kinds[entry.detail.kind]
|
|
497
|
+
// Convert duration from milliseconds to seconds
|
|
498
|
+
histogram.observe({
|
|
499
|
+
[kind]: entry.duration / 1000
|
|
500
|
+
})
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
obs.observe({ entryTypes: ['gc'] })
|
|
504
|
+
}
|