@aidra/pmx-io 0.0.0-stage → 9.13.0
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 +69 -2
- package/bin/pmx-io-node.js +143 -0
- package/index.d.ts +381 -0
- package/index.js +27 -0
- package/index.mjs +47 -0
- package/lib/actions.js +120 -0
- package/lib/activate.js +402 -0
- package/lib/application.js +106 -0
- package/lib/bun.js +155 -0
- package/lib/config.js +67 -0
- package/lib/constants.js +17 -0
- package/lib/debug.js +48 -0
- package/lib/esm-hooks.mjs +111 -0
- package/lib/exit.js +79 -0
- package/lib/features/metrics.js +409 -0
- package/lib/features/notify.js +358 -0
- package/lib/features/profiling.js +208 -0
- package/lib/hooks.js +151 -0
- package/lib/inbound.js +174 -0
- package/lib/integrations.js +372 -0
- package/lib/itg/amqplib.js +169 -0
- package/lib/itg/bullmq.js +141 -0
- package/lib/itg/common.js +657 -0
- package/lib/itg/dbcomment.js +114 -0
- package/lib/itg/index.js +103 -0
- package/lib/itg/ioredis.js +212 -0
- package/lib/itg/kafkajs.js +205 -0
- package/lib/itg/mongodb.js +322 -0
- package/lib/itg/mysql2.js +205 -0
- package/lib/itg/nats.js +531 -0
- package/lib/itg/noderedis.js +236 -0
- package/lib/itg/pg.js +253 -0
- package/lib/itg/pgqueue.js +305 -0
- package/lib/itg/postgres.js +252 -0
- package/lib/itg/redis-common.js +265 -0
- package/lib/itg/sql.js +302 -0
- package/lib/metrics/service.js +286 -0
- package/lib/metrics/types.js +342 -0
- package/lib/modules.js +260 -0
- package/lib/node-options.js +62 -0
- package/lib/otel/bridge.js +797 -0
- package/lib/otel/env.js +114 -0
- package/lib/outbound.js +362 -0
- package/lib/pmx.js +757 -0
- package/lib/relay/hub.js +279 -0
- package/lib/relay/transport.js +253 -0
- package/lib/standalone/activate.js +307 -0
- package/lib/standalone/capture.js +55 -0
- package/lib/standalone/container.js +909 -0
- package/lib/standalone/transport.js +1023 -0
- package/lib/tracing/context.js +90 -0
- package/lib/tracing/control.js +124 -0
- package/lib/tracing/exporter.js +115 -0
- package/lib/tracing/index.js +242 -0
- package/lib/tracing/logs.js +176 -0
- package/lib/tracing/options.js +105 -0
- package/lib/tracing/paths.js +34 -0
- package/lib/tracing/tracer.js +850 -0
- package/lib/tracing/w3c.js +415 -0
- package/lib/transport/ipc.js +97 -0
- package/lib/transport/ws.js +511 -0
- package/lib/util.js +109 -0
- package/package.json +48 -4
- package/preload.cjs +26 -0
- package/preload.mjs +25 -0
- package/standalone.cjs +16 -0
- package/standalone.mjs +17 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
// Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// Default metrics (80 A.4, NIO-050..NIO-062): event loop, V8 heap, HTTP/HTTPS, network,
|
|
5
|
+
// runtime (GC, rusage) and file descriptors. Names, ids, units and value types are exact.
|
|
6
|
+
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
const { Histogram, Meter } = require('../metrics/types')
|
|
9
|
+
const { round2 } = require('../util')
|
|
10
|
+
const hooks = require('../hooks')
|
|
11
|
+
const debug = require('../debug')('axm:features:metrics')
|
|
12
|
+
|
|
13
|
+
function every (ms, fn) {
|
|
14
|
+
const t = setInterval(fn, ms)
|
|
15
|
+
if (t.unref) t.unref()
|
|
16
|
+
return t
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function after (ms, fn) {
|
|
20
|
+
const t = setTimeout(fn, ms)
|
|
21
|
+
if (t.unref) t.unref()
|
|
22
|
+
return t
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function str2 (n) {
|
|
26
|
+
return typeof n === 'number' && Number.isFinite(n) ? n.toFixed(2) : undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Event loop: active handles/requests (A.4.1), latency (A.4.2, NIO-052)
|
|
31
|
+
|
|
32
|
+
function activeCounters () {
|
|
33
|
+
if (typeof process._getActiveHandles === 'function' && typeof process._getActiveRequests === 'function') {
|
|
34
|
+
return {
|
|
35
|
+
handles: () => process._getActiveHandles().length,
|
|
36
|
+
requests: () => process._getActiveRequests().length
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (typeof process.getActiveResourcesInfo === 'function') {
|
|
40
|
+
const isReq = (t) => /Req|Request|Wrap$/.test(t) && t !== 'TTYWrap'
|
|
41
|
+
return {
|
|
42
|
+
handles: () => process.getActiveResourcesInfo().filter((t) => t !== 'Timeout' && t !== 'Immediate' && !isReq(t)).length,
|
|
43
|
+
requests: () => process.getActiveResourcesInfo().filter(isReq).length
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function initEventLoop (ctx, cfg) {
|
|
50
|
+
let active = true
|
|
51
|
+
let delay = true
|
|
52
|
+
if (cfg === false) return
|
|
53
|
+
if (cfg !== null && typeof cfg === 'object') {
|
|
54
|
+
active = cfg.eventLoopActive === true
|
|
55
|
+
delay = cfg.eventLoopDelay !== false
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (active) {
|
|
59
|
+
const counters = activeCounters()
|
|
60
|
+
if (counters) {
|
|
61
|
+
let handles
|
|
62
|
+
let requests
|
|
63
|
+
const sample = () => {
|
|
64
|
+
try {
|
|
65
|
+
handles = counters.handles()
|
|
66
|
+
requests = counters.requests()
|
|
67
|
+
} catch (_) {}
|
|
68
|
+
}
|
|
69
|
+
sample()
|
|
70
|
+
ctx.timers.push(every(1000, sample))
|
|
71
|
+
ctx.service.register({ name: 'Active handles', id: 'internal/libuv/handles', handler: () => handles })
|
|
72
|
+
ctx.service.register({ name: 'Active requests', id: 'internal/libuv/requests', handler: () => requests })
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (delay) {
|
|
77
|
+
const hist = new Histogram({ measurement: 'median' })
|
|
78
|
+
let last = process.hrtime()
|
|
79
|
+
ctx.timers.push(every(1000, () => {
|
|
80
|
+
const d = process.hrtime(last)
|
|
81
|
+
last = process.hrtime()
|
|
82
|
+
hist.update(d[0] * 1e3 + d[1] / 1e6 - 1000)
|
|
83
|
+
}))
|
|
84
|
+
const pct = (p) => () => (hist.getCount() === 0 ? undefined : str2(hist.percentiles([p])[p]))
|
|
85
|
+
ctx.service.register({ name: 'Event Loop Latency', id: 'internal/libuv/latency/p50', unit: 'ms', handler: pct(0.5) })
|
|
86
|
+
ctx.service.register({ name: 'Event Loop Latency p95', id: 'internal/libuv/latency/p95', unit: 'ms', handler: pct(0.95) })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// V8 heap (A.4.3)
|
|
92
|
+
|
|
93
|
+
function initV8 (ctx, cfg) {
|
|
94
|
+
if (cfg === false) return
|
|
95
|
+
let v8
|
|
96
|
+
try {
|
|
97
|
+
v8 = require('v8')
|
|
98
|
+
if (typeof v8.getHeapStatistics !== 'function') return
|
|
99
|
+
v8.getHeapStatistics()
|
|
100
|
+
} catch (_) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
const on = (key) => !(cfg !== null && typeof cfg === 'object' && cfg[key] === false)
|
|
104
|
+
let stats = null
|
|
105
|
+
const sample = () => {
|
|
106
|
+
try {
|
|
107
|
+
stats = v8.getHeapStatistics()
|
|
108
|
+
} catch (_) {}
|
|
109
|
+
}
|
|
110
|
+
sample()
|
|
111
|
+
ctx.timers.push(every(800, sample))
|
|
112
|
+
const MiB = 1048576
|
|
113
|
+
if (on('total_heap_size')) {
|
|
114
|
+
ctx.service.register({ name: 'Heap Size', id: 'internal/v8/heap/total', unit: 'MiB', handler: () => stats && str2(stats.total_heap_size / MiB) })
|
|
115
|
+
}
|
|
116
|
+
if (on('used_heap_size')) {
|
|
117
|
+
ctx.service.register({ name: 'Used Heap Size', id: 'internal/v8/heap/used', unit: 'MiB', handler: () => stats && str2(stats.used_heap_size / MiB) })
|
|
118
|
+
}
|
|
119
|
+
if (on('heap_used_percent')) {
|
|
120
|
+
ctx.service.register({
|
|
121
|
+
name: 'Heap Usage',
|
|
122
|
+
id: 'internal/v8/heap/usage',
|
|
123
|
+
unit: '%',
|
|
124
|
+
handler: () => stats && stats.total_heap_size > 0 ? round2(stats.used_heap_size / stats.total_heap_size * 100) : undefined
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// HTTP / HTTPS servers (A.4.4, NIO-053)
|
|
131
|
+
|
|
132
|
+
function initHttp (ctx, cfg) {
|
|
133
|
+
if (cfg === false) return false
|
|
134
|
+
const per = {}
|
|
135
|
+
for (const protocol of ['http', 'https']) {
|
|
136
|
+
const label = protocol.toUpperCase()
|
|
137
|
+
const meter = new Meter()
|
|
138
|
+
const hist = new Histogram({ measurement: 'median' })
|
|
139
|
+
per[protocol] = { meter, hist }
|
|
140
|
+
ctx.stoppables.push(meter)
|
|
141
|
+
ctx.service.instrument(`internal/${protocol}/builtin/reqs`, 'meter', meter)
|
|
142
|
+
ctx.service.instrument(`internal/${protocol}/builtin/latency`, 'histogram', hist)
|
|
143
|
+
ctx.service.register({ name: label, id: `internal/${protocol}/builtin/reqs`, unit: 'req/min', implementation: meter, handler: () => (meter.isUsed() ? meter.val() : undefined) })
|
|
144
|
+
ctx.service.register({
|
|
145
|
+
name: `${label} Mean Latency`,
|
|
146
|
+
id: `internal/${protocol}/builtin/latency/p50`,
|
|
147
|
+
unit: 'ms',
|
|
148
|
+
handler: () => (hist.getCount() === 0 ? undefined : hist.percentiles([0.5])[0.5])
|
|
149
|
+
})
|
|
150
|
+
ctx.service.register({
|
|
151
|
+
name: `${label} P95 Latency`,
|
|
152
|
+
id: `internal/${protocol}/builtin/latency/p95`,
|
|
153
|
+
unit: 'ms',
|
|
154
|
+
handler: () => (hist.getCount() === 0 ? undefined : hist.percentiles([0.95])[0.95])
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
const inbound = require('../inbound')
|
|
158
|
+
inbound.setConsumer('metrics', {
|
|
159
|
+
request (protocol, req, res) {
|
|
160
|
+
const m = per[protocol]
|
|
161
|
+
if (!m) return
|
|
162
|
+
m.meter.mark()
|
|
163
|
+
if (!res || typeof res.once !== 'function') return
|
|
164
|
+
const start = Date.now()
|
|
165
|
+
res.once('finish', () => m.hist.update(Date.now() - start))
|
|
166
|
+
},
|
|
167
|
+
// Bun.serve (NIO-101): returns the function to call when the response is returned
|
|
168
|
+
start (protocol) {
|
|
169
|
+
const m = per[protocol]
|
|
170
|
+
if (!m) return null
|
|
171
|
+
m.meter.mark()
|
|
172
|
+
const start = Date.now()
|
|
173
|
+
return () => m.hist.update(Date.now() - start)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
if (process.versions.bun) require('../bun').install()
|
|
177
|
+
ctx.cleanups.push(() => inbound.setConsumer('metrics', null))
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Outbound HTTP (NIO-055)
|
|
183
|
+
|
|
184
|
+
function initHttpOutbound (ctx, cfg) {
|
|
185
|
+
if (cfg === false) return
|
|
186
|
+
const meter = new Meter()
|
|
187
|
+
const hist = new Histogram({ measurement: 'median' })
|
|
188
|
+
ctx.stoppables.push(meter)
|
|
189
|
+
ctx.service.register({ name: 'HTTP Outbound', id: 'internal/http/outbound/reqs', unit: 'req/s', implementation: meter, handler: () => (meter.isUsed() ? meter.val() : undefined) })
|
|
190
|
+
ctx.service.register({
|
|
191
|
+
name: 'HTTP Outbound Mean Latency',
|
|
192
|
+
id: 'internal/http/outbound/latency/p50',
|
|
193
|
+
unit: 'ms',
|
|
194
|
+
handler: () => (hist.getCount() === 0 ? undefined : round2(hist.percentiles([0.5])[0.5]))
|
|
195
|
+
})
|
|
196
|
+
ctx.service.register({
|
|
197
|
+
name: 'HTTP Outbound P95 Latency',
|
|
198
|
+
id: 'internal/http/outbound/latency/p95',
|
|
199
|
+
unit: 'ms',
|
|
200
|
+
handler: () => (hist.getCount() === 0 ? undefined : round2(hist.percentiles([0.95])[0.95]))
|
|
201
|
+
})
|
|
202
|
+
ctx.service.instrument('internal/http/outbound/reqs', 'meter', meter)
|
|
203
|
+
ctx.service.instrument('internal/http/outbound/latency', 'histogram', hist)
|
|
204
|
+
const outbound = require('../outbound')
|
|
205
|
+
outbound.setConsumer('metrics', (ms) => {
|
|
206
|
+
meter.mark()
|
|
207
|
+
hist.update(ms)
|
|
208
|
+
})
|
|
209
|
+
ctx.cleanups.push(() => outbound.setConsumer('metrics', null))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Network (A.4.5, opt-in)
|
|
214
|
+
|
|
215
|
+
function initNetwork (ctx, cfg) {
|
|
216
|
+
let upload = false
|
|
217
|
+
let download = false
|
|
218
|
+
if (cfg === true) {
|
|
219
|
+
upload = download = true
|
|
220
|
+
} else if (cfg !== null && typeof cfg === 'object') {
|
|
221
|
+
upload = cfg.upload === true
|
|
222
|
+
download = cfg.download === true
|
|
223
|
+
}
|
|
224
|
+
if (!upload && !download) return
|
|
225
|
+
const kb = (meter) => () => (meter.isUsed() ? Math.floor(meter.rawVal() / 1024 * 1000) / 1000 : undefined)
|
|
226
|
+
let inMeter, outMeter
|
|
227
|
+
if (download) {
|
|
228
|
+
inMeter = new Meter()
|
|
229
|
+
ctx.stoppables.push(inMeter)
|
|
230
|
+
ctx.service.register({ name: 'Network In', id: 'internal/network/in', unit: 'kb/s', implementation: inMeter, handler: kb(inMeter) })
|
|
231
|
+
}
|
|
232
|
+
if (upload) {
|
|
233
|
+
outMeter = new Meter()
|
|
234
|
+
ctx.stoppables.push(outMeter)
|
|
235
|
+
ctx.service.register({ name: 'Network Out', id: 'internal/network/out', unit: 'kb/s', implementation: outMeter, handler: kb(outMeter) })
|
|
236
|
+
}
|
|
237
|
+
ctx.timers.push(after(500, () => {
|
|
238
|
+
if (ctx.destroyed) return
|
|
239
|
+
hooks.installNet()
|
|
240
|
+
if (inMeter) hooks.setHandler('socketRead', (n) => inMeter.mark(n))
|
|
241
|
+
if (outMeter) hooks.setHandler('socketWrite', (n) => outMeter.mark(n))
|
|
242
|
+
}))
|
|
243
|
+
ctx.cleanups.push(() => {
|
|
244
|
+
hooks.setHandler('socketRead', null)
|
|
245
|
+
hooks.setHandler('socketWrite', null)
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Runtime: GC pauses and rusage deltas without a native addon (NIO-060..062)
|
|
251
|
+
|
|
252
|
+
function initRuntime (ctx, cfg) {
|
|
253
|
+
if (cfg === false) return
|
|
254
|
+
const on = (key) => cfg === true || cfg === undefined || cfg === null || typeof cfg !== 'object' || cfg[key] === true
|
|
255
|
+
const register = (name, id, unit, handler) => ctx.service.register({ name, id, unit, handler })
|
|
256
|
+
const pctHandler = (hist, p) => () => (hist.getCount() === 0 ? undefined : hist.percentiles([p])[p])
|
|
257
|
+
|
|
258
|
+
if (on('gcNewPause') || on('gcOldPause')) {
|
|
259
|
+
try {
|
|
260
|
+
const { PerformanceObserver, constants } = require('perf_hooks')
|
|
261
|
+
const MINOR = (constants && constants.NODE_PERFORMANCE_GC_MINOR) || 1
|
|
262
|
+
const MAJOR = (constants && constants.NODE_PERFORMANCE_GC_MAJOR) || 4
|
|
263
|
+
const newHist = new Histogram({ measurement: 'median' })
|
|
264
|
+
const oldHist = new Histogram({ measurement: 'median' })
|
|
265
|
+
const obs = new PerformanceObserver((list) => {
|
|
266
|
+
for (const e of list.getEntries()) {
|
|
267
|
+
const kind = e.detail && e.detail.kind !== undefined ? e.detail.kind : e.kind
|
|
268
|
+
if (kind === MINOR) newHist.update(e.duration)
|
|
269
|
+
else if (kind === MAJOR) oldHist.update(e.duration)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
obs.observe({ entryTypes: ['gc'] })
|
|
273
|
+
ctx.cleanups.push(() => obs.disconnect())
|
|
274
|
+
if (on('gcNewPause')) {
|
|
275
|
+
register('GC New Space Pause', 'internal/v8/gc/new/pause/p50', 'ms', pctHandler(newHist, 0.5))
|
|
276
|
+
register('GC New Space Pause p95', 'internal/v8/gc/new/pause/p95', 'ms', pctHandler(newHist, 0.95))
|
|
277
|
+
}
|
|
278
|
+
if (on('gcOldPause')) {
|
|
279
|
+
register('GC Old Space Pause', 'internal/v8/gc/old/pause/p50', 'ms', pctHandler(oldHist, 0.5))
|
|
280
|
+
register('GC Old Space Pause p95', 'internal/v8/gc/old/pause/p95', 'ms', pctHandler(oldHist, 0.95))
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
debug('gc observation unavailable: %s', err && err.message)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const wantCs = on('contextSwitchs')
|
|
288
|
+
const wantPf = on('pageFaults')
|
|
289
|
+
if ((wantCs || wantPf) && typeof process.resourceUsage === 'function') {
|
|
290
|
+
// the first resource-usage sample (~3 ms) waits until the app's main module has started
|
|
291
|
+
// (NIO-024); the first metric push is 990 ms away anyway
|
|
292
|
+
const t = setImmediate(() => {
|
|
293
|
+
if (!ctx.destroyed) initRusage(ctx, wantCs, wantPf, register)
|
|
294
|
+
})
|
|
295
|
+
if (t.unref) t.unref()
|
|
296
|
+
ctx.cleanups.push(() => clearImmediate(t))
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function initRusage (ctx, wantCs, wantPf, register) {
|
|
301
|
+
const hists = {
|
|
302
|
+
vol: new Histogram({ measurement: 'mean' }),
|
|
303
|
+
invol: new Histogram({ measurement: 'mean' }),
|
|
304
|
+
minor: new Histogram({ measurement: 'mean' }),
|
|
305
|
+
major: new Histogram({ measurement: 'mean' })
|
|
306
|
+
}
|
|
307
|
+
let prev
|
|
308
|
+
try {
|
|
309
|
+
prev = process.resourceUsage()
|
|
310
|
+
} catch (_) {
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
ctx.timers.push(every(1000, () => {
|
|
314
|
+
let cur
|
|
315
|
+
try {
|
|
316
|
+
cur = process.resourceUsage()
|
|
317
|
+
} catch (_) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
hists.vol.update(cur.voluntaryContextSwitches - prev.voluntaryContextSwitches)
|
|
321
|
+
hists.invol.update(cur.involuntaryContextSwitches - prev.involuntaryContextSwitches)
|
|
322
|
+
hists.minor.update(cur.minorPageFault - prev.minorPageFault)
|
|
323
|
+
hists.major.update(cur.majorPageFault - prev.majorPageFault)
|
|
324
|
+
prev = cur
|
|
325
|
+
}))
|
|
326
|
+
const mean = (h) => () => (h.getCount() === 0 ? undefined : round2(h.getMean()))
|
|
327
|
+
if (wantCs) {
|
|
328
|
+
register('Volontary CPU Context Switch', 'internal/uv/cpu/contextswitch/volontary', undefined, mean(hists.vol))
|
|
329
|
+
register('Involuntary CPU Context Switch', 'internal/uv/cpu/contextswitch/involontary', undefined, mean(hists.invol))
|
|
330
|
+
}
|
|
331
|
+
if (wantPf) {
|
|
332
|
+
register('Minor Page Fault', 'internal/uv/memory/pagefault/minor', undefined, mean(hists.minor))
|
|
333
|
+
register('Major Page Fault', 'internal/uv/memory/pagefault/major', undefined, mean(hists.major))
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// File descriptors (NIO-054)
|
|
339
|
+
|
|
340
|
+
function initFds (ctx, cfg) {
|
|
341
|
+
if (cfg === false) return
|
|
342
|
+
let dir = null
|
|
343
|
+
for (const d of ['/proc/self/fd', '/dev/fd']) {
|
|
344
|
+
try {
|
|
345
|
+
fs.readdirSync(d)
|
|
346
|
+
dir = d
|
|
347
|
+
break
|
|
348
|
+
} catch (_) {}
|
|
349
|
+
}
|
|
350
|
+
if (!dir) return
|
|
351
|
+
let count
|
|
352
|
+
const sample = () => {
|
|
353
|
+
try {
|
|
354
|
+
// The listing itself opens one descriptor, which is subtracted.
|
|
355
|
+
count = fs.readdirSync(dir).length - 1
|
|
356
|
+
} catch (_) {}
|
|
357
|
+
}
|
|
358
|
+
sample()
|
|
359
|
+
ctx.timers.push(every(5000, sample))
|
|
360
|
+
ctx.service.register({ name: 'File Descriptors', id: 'internal/process/fds', handler: () => count })
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Initialise the default metrics. Returns a handle with destroy() and whether
|
|
365
|
+
* HTTP measurement is on (for the `latency` option message).
|
|
366
|
+
*/
|
|
367
|
+
function initDefaultMetrics (service, metricsCfg, opts) {
|
|
368
|
+
const relay = Boolean(opts && opts.relay)
|
|
369
|
+
const cfg = metricsCfg !== null && typeof metricsCfg === 'object' ? metricsCfg : {}
|
|
370
|
+
const ctx = { service, timers: [], stoppables: [], cleanups: [], destroyed: false }
|
|
371
|
+
const guard = (name, fn) => {
|
|
372
|
+
try {
|
|
373
|
+
return fn()
|
|
374
|
+
} catch (err) {
|
|
375
|
+
debug('metric group %s failed: %s', name, err && err.message)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// relays contribute request metrics only; process-level metrics stay the root's (NIO-291/292)
|
|
379
|
+
if (!relay) {
|
|
380
|
+
const processGroups = () => {
|
|
381
|
+
if (ctx.destroyed) return
|
|
382
|
+
guard('eventLoop', () => initEventLoop(ctx, cfg.eventLoop))
|
|
383
|
+
guard('v8', () => initV8(ctx, cfg.v8))
|
|
384
|
+
guard('runtime', () => initRuntime(ctx, cfg.runtime))
|
|
385
|
+
guard('fds', () => initFds(ctx, cfg.fds))
|
|
386
|
+
guard('network', () => initNetwork(ctx, cfg.network))
|
|
387
|
+
}
|
|
388
|
+
// `later` (the instance's deferred steps): not needed before the app's first line (NIO-024)
|
|
389
|
+
if (opts && typeof opts.later === 'function') opts.later(processGroups)
|
|
390
|
+
else processGroups()
|
|
391
|
+
}
|
|
392
|
+
const http = guard('http', () => initHttp(ctx, cfg.http)) === true
|
|
393
|
+
guard('httpOutbound', () => initHttpOutbound(ctx, cfg.httpOutbound))
|
|
394
|
+
return {
|
|
395
|
+
http,
|
|
396
|
+
destroy () {
|
|
397
|
+
ctx.destroyed = true
|
|
398
|
+
for (const t of ctx.timers) clearTimeout(t)
|
|
399
|
+
for (const m of ctx.stoppables) m._stop()
|
|
400
|
+
for (const fn of ctx.cleanups) {
|
|
401
|
+
try {
|
|
402
|
+
fn()
|
|
403
|
+
} catch (_) {}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
module.exports = { initDefaultMetrics }
|