@aidra/pmx-io 9.19.0 → 9.21.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/index.d.ts +6 -0
- package/lib/activate.js +4 -2
- package/lib/bun.js +13 -1
- package/lib/callmap.js +427 -0
- package/lib/esm-hooks.mjs +3 -3
- package/lib/inbound.js +19 -5
- package/lib/itg/common.js +89 -2
- package/lib/itg/ioredis.js +36 -23
- package/lib/itg/mongodb.js +37 -3
- package/lib/itg/mysql2.js +26 -20
- package/lib/itg/noderedis.js +20 -13
- package/lib/itg/pg.js +19 -15
- package/lib/itg/postgres.js +17 -13
- package/lib/modules.js +18 -3
- package/lib/outbound.js +26 -8
- package/lib/pmx.js +55 -2
- package/lib/relay/transport.js +1 -1
- package/lib/standalone/transport.js +17 -0
- package/lib/tracing/control.js +11 -5
- package/package.json +1 -1
- package/preload.mjs +2 -1
package/lib/pmx.js
CHANGED
|
@@ -14,6 +14,7 @@ const { NotifyFeature } = require('./features/notify')
|
|
|
14
14
|
const { initDefaultMetrics } = require('./features/metrics')
|
|
15
15
|
const { resolveTracing } = require('./tracing/options')
|
|
16
16
|
const control = require('./tracing/control')
|
|
17
|
+
const callmap = require('./callmap')
|
|
17
18
|
// loaded on first use: the propagation API is not needed before the app's first line (NIO-024)
|
|
18
19
|
let w3cModule = null
|
|
19
20
|
const w3c = new Proxy({}, { get: (_, k) => (w3cModule || (w3cModule = require('./tracing/w3c')))[k] })
|
|
@@ -43,7 +44,7 @@ const PUBLIC_METHODS = [
|
|
|
43
44
|
|
|
44
45
|
const REPORTED = Symbol.for('@pmx/io:reported')
|
|
45
46
|
// G.5: the app → daemon envelopes a relay may forward (anything else from a relay is ignored)
|
|
46
|
-
const RELAYED_TYPES = ['process:exception', 'human:event', 'trace-span', 'trace-spans']
|
|
47
|
+
const RELAYED_TYPES = ['process:exception', 'human:event', 'trace-span', 'trace-spans', 'service:calls']
|
|
47
48
|
|
|
48
49
|
function profilingEnabled (p) {
|
|
49
50
|
if (p === false) return false
|
|
@@ -117,6 +118,9 @@ class PMX {
|
|
|
117
118
|
// 80 NIO-298: the prodman layer (undefined until the first init reads PMX_IO_TRACING)
|
|
118
119
|
this._tracingLayer = undefined
|
|
119
120
|
this._appTracing = { value: undefined, has: false }
|
|
121
|
+
// 83 CMP-001: the app's init({ serviceMap }) and the resolved { value, source }
|
|
122
|
+
this._appServiceMap = undefined
|
|
123
|
+
this._serviceMap = { value: false, source: 'default' }
|
|
120
124
|
this._defaultMetrics = null
|
|
121
125
|
for (const m of PUBLIC_METHODS) this[m] = this[m].bind(this)
|
|
122
126
|
this.Entrypoint = makeEntrypoint(this)
|
|
@@ -238,6 +242,7 @@ class PMX {
|
|
|
238
242
|
if (this._initialized) this._teardown(true)
|
|
239
243
|
const hasTracing = config !== null && typeof config === 'object' && Object.prototype.hasOwnProperty.call(config, 'tracing')
|
|
240
244
|
const appTracing = hasTracing ? config.tracing : undefined
|
|
245
|
+
this._appServiceMap = config !== null && typeof config === 'object' ? config.serviceMap : undefined
|
|
241
246
|
config = normalizeConfig(config)
|
|
242
247
|
// NIO-080/NIO-201: environment sources, then the app's `tracing` key (kept when absent)
|
|
243
248
|
config.tracing = resolveTracing(appTracing, hasTracing)
|
|
@@ -314,6 +319,8 @@ class PMX {
|
|
|
314
319
|
if (process.versions.bun) require('./bun').install()
|
|
315
320
|
}
|
|
316
321
|
if (config.tracing && config.tracing.enabled === true) this._startTracing(config)
|
|
322
|
+
// 83: the call map, with tracing on or off
|
|
323
|
+
this._applyServiceMap()
|
|
317
324
|
|
|
318
325
|
// 6. full configuration
|
|
319
326
|
this._initialized = true
|
|
@@ -370,11 +377,46 @@ class PMX {
|
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
379
|
|
|
380
|
+
/** 83 CMP-001/CMP-003: switch the call map on or off as the layer, the config and the env say. */
|
|
381
|
+
_applyServiceMap () {
|
|
382
|
+
const r = callmap.resolve(this._tracingLayer, this._appServiceMap)
|
|
383
|
+
this._serviceMap = r
|
|
384
|
+
const inbound = require('./inbound')
|
|
385
|
+
const outbound = require('./outbound')
|
|
386
|
+
const modules = require('./modules')
|
|
387
|
+
const t = this._transport
|
|
388
|
+
if (r.value === true) {
|
|
389
|
+
callmap.setEnabled(true, (data) => {
|
|
390
|
+
if (this._transport) this._transport.send('service:calls', data)
|
|
391
|
+
})
|
|
392
|
+
inbound.setConsumer('callmap', callmap)
|
|
393
|
+
outbound.setConsumer('callmap', callmap)
|
|
394
|
+
modules.enableCallMap()
|
|
395
|
+
if (process.versions.bun) require('./bun').install()
|
|
396
|
+
// standalone: the partial minute goes before agent:shutdown (NIO-268)
|
|
397
|
+
if (t && typeof t.beforeShutdown === 'function') {
|
|
398
|
+
if (!this._callMapFlush) this._callMapFlush = () => callmap.flush(true)
|
|
399
|
+
t.beforeShutdown(this._callMapFlush)
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
inbound.setConsumer('callmap', null)
|
|
403
|
+
outbound.setConsumer('callmap', null)
|
|
404
|
+
modules.disableCallMap()
|
|
405
|
+
callmap.setEnabled(false)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** True while the call map is on (preload.mjs registers the module hooks then, CMP-003). */
|
|
410
|
+
_serviceMapEnabled () {
|
|
411
|
+
return Boolean(this._initialized && callmap.state.on)
|
|
412
|
+
}
|
|
413
|
+
|
|
373
414
|
/** 80 NIO-298: replace the prodman layer and apply it at once (no restart). */
|
|
374
415
|
_setTracingLayer (layer) {
|
|
375
416
|
this._tracingLayer = control.cleanLayer(layer)
|
|
376
417
|
control.exportLayer(this._tracingLayer)
|
|
377
418
|
if (!this._initialized || this._isRelay()) return
|
|
419
|
+
this._applyServiceMap()
|
|
378
420
|
const t = control.applyLayer(resolveTracing(this._appTracing.value, this._appTracing.has), this._tracingLayer)
|
|
379
421
|
this._config.tracing = t
|
|
380
422
|
const want = t.enabled === true
|
|
@@ -415,7 +457,7 @@ class PMX {
|
|
|
415
457
|
_reportTracing () {
|
|
416
458
|
if (!this._transport || !this._config || this._isRelay()) return
|
|
417
459
|
const src = control.sources(this._appTracing.value, this._appTracing.has)
|
|
418
|
-
this._transport.send('axm:option:configuration', { tracing_effective: control.report(this._config.tracing, src, this._tracingLayer || {}) })
|
|
460
|
+
this._transport.send('axm:option:configuration', { tracing_effective: control.report(this._config.tracing, src, this._tracingLayer || {}, this._serviceMap) })
|
|
419
461
|
}
|
|
420
462
|
|
|
421
463
|
_reportDependencies (generation) {
|
|
@@ -465,6 +507,17 @@ class PMX {
|
|
|
465
507
|
safe(() => this._notify && this._notify.destroy())
|
|
466
508
|
safe(() => this._actions && this._actions.destroy())
|
|
467
509
|
safe(() => require('./inbound').setConsumer('errors', null))
|
|
510
|
+
if (!keepTransport) {
|
|
511
|
+
// destroy(): the call map's records are sent now and recording stops (a re-init keeps them;
|
|
512
|
+
// init applies the switch again at once)
|
|
513
|
+
safe(() => {
|
|
514
|
+
require('./inbound').setConsumer('callmap', null)
|
|
515
|
+
require('./outbound').setConsumer('callmap', null)
|
|
516
|
+
require('./modules').disableCallMap()
|
|
517
|
+
callmap.stop()
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
safe(() => this._callMapFlush && this._transport && typeof this._transport.offBeforeShutdown === 'function' && this._transport.offBeforeShutdown(this._callMapFlush))
|
|
468
521
|
// the standalone root keeps its prodman connection across re-inits (destroyed by destroy())
|
|
469
522
|
if (this._mode !== 'standalone') safe(() => this._transport && this._transport.destroy())
|
|
470
523
|
this._tracing = this._defaultMetrics = this._metrics = this._profiling = null
|
package/lib/relay/transport.js
CHANGED
|
@@ -14,7 +14,7 @@ const net = require('net')
|
|
|
14
14
|
const { RELAY_KEY, socketPath, helloLine } = require('./hub')
|
|
15
15
|
const debug = require('../debug')('axm:relay')
|
|
16
16
|
|
|
17
|
-
const FORWARDED = ['process:exception', 'human:event', 'trace-span']
|
|
17
|
+
const FORWARDED = ['process:exception', 'human:event', 'trace-span', 'service:calls']
|
|
18
18
|
const MAX_QUEUE = 1000
|
|
19
19
|
|
|
20
20
|
function wrapMessage (type, data, extraMeta) {
|
|
@@ -630,6 +630,7 @@ class ProdmanTransport {
|
|
|
630
630
|
}
|
|
631
631
|
case 'trace-span':
|
|
632
632
|
case 'trace-spans':
|
|
633
|
+
case 'service:calls': // 83 CMP-021
|
|
633
634
|
case 'axm:reply':
|
|
634
635
|
case 'profilings':
|
|
635
636
|
case 'application:dependencies':
|
|
@@ -654,6 +655,16 @@ class ProdmanTransport {
|
|
|
654
655
|
this._listeners.push(fn)
|
|
655
656
|
}
|
|
656
657
|
|
|
658
|
+
/** `fn()` runs when shutdown starts, before agent:shutdown is queued (once per registration). */
|
|
659
|
+
beforeShutdown (fn) {
|
|
660
|
+
if (!this._beforeShutdown) this._beforeShutdown = []
|
|
661
|
+
if (!this._beforeShutdown.includes(fn)) this._beforeShutdown.push(fn)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
offBeforeShutdown (fn) {
|
|
665
|
+
if (this._beforeShutdown) this._beforeShutdown = this._beforeShutdown.filter((f) => f !== fn)
|
|
666
|
+
}
|
|
667
|
+
|
|
657
668
|
offMessage (fn) {
|
|
658
669
|
this._listeners = this._listeners.filter((f) => f !== fn)
|
|
659
670
|
}
|
|
@@ -951,6 +962,12 @@ class ProdmanTransport {
|
|
|
951
962
|
try {
|
|
952
963
|
this._flushLogs(true)
|
|
953
964
|
} catch (_) {}
|
|
965
|
+
// the instance's last data (83 CMP-020: the call map's partial minute) before agent:shutdown
|
|
966
|
+
for (const fn of (this._beforeShutdown || []).slice()) {
|
|
967
|
+
try {
|
|
968
|
+
fn()
|
|
969
|
+
} catch (_) {}
|
|
970
|
+
}
|
|
954
971
|
this.packet('agent:shutdown', this._shutdownPayload, null, false)
|
|
955
972
|
return this.flush(flushMs === undefined ? 1000 : flushMs).then(() => {
|
|
956
973
|
this.close(1000, 'Shutting down')
|
package/lib/tracing/control.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
// Tracing control from prodman (80 NIO-298, INJ-026; 40 §8.6; prodman 02 §6.19 PROTO-160…163).
|
|
5
|
-
// The *prodman layer* holds up to
|
|
6
|
-
// beats every other source (the app's init(config) included); below it the NIO-201 order is kept.
|
|
5
|
+
// The *prodman layer* holds up to four wire keys — `enabled`, `sample_ratio`, `db_comment` and
|
|
6
|
+
// `service_map` (the call map, 83 CMP-001) — and beats every other source (the app's init(config) included); below it the NIO-201 order is kept.
|
|
7
7
|
// Sources reported in `tracing_effective`: prodman | config | env | default.
|
|
8
8
|
|
|
9
9
|
const { envIoConf } = require('./options')
|
|
@@ -21,6 +21,7 @@ function cleanLayer (v) {
|
|
|
21
21
|
if (typeof v.enabled === 'boolean') out.enabled = v.enabled
|
|
22
22
|
if (typeof v.sample_ratio === 'number' && Number.isFinite(v.sample_ratio)) out.sample_ratio = Math.min(1, Math.max(0, v.sample_ratio))
|
|
23
23
|
if (typeof v.db_comment === 'string' && MODES.includes(v.db_comment)) out.db_comment = v.db_comment
|
|
24
|
+
if (typeof v.service_map === 'boolean') out.service_map = v.service_map
|
|
24
25
|
return out
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -107,18 +108,23 @@ function sources (appValue, hasKey, env) {
|
|
|
107
108
|
return src
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
/**
|
|
111
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The `tracing_effective` report (PROTO-163) for the resolved `tracing` object; `serviceMap`
|
|
113
|
+
* ({ value, source }, 83 CMP-001) is added as `service_map` when given.
|
|
114
|
+
*/
|
|
115
|
+
function report (tracing, src, layer, serviceMap) {
|
|
112
116
|
const { normMode } = require('../itg/dbcomment')
|
|
113
117
|
const ratio = Number(tracing.sampleRatio)
|
|
114
118
|
const off = dbOff(tracing.db)
|
|
115
119
|
const s = (k) => (layer[k] !== undefined && !(k === 'db_comment' && off) ? 'prodman' : src[k])
|
|
116
|
-
|
|
120
|
+
const out = {
|
|
117
121
|
v: 1,
|
|
118
122
|
enabled: { value: tracing.enabled === true, source: s('enabled') },
|
|
119
123
|
sample_ratio: { value: tracing.sampleRatio !== undefined && Number.isFinite(ratio) ? Math.min(1, Math.max(0, ratio)) : 1, source: s('sample_ratio') },
|
|
120
124
|
db_comment: { value: off ? 'off' : normMode(isObj(tracing.db) ? tracing.db.comment : undefined), source: s('db_comment') }
|
|
121
125
|
}
|
|
126
|
+
if (serviceMap) out.service_map = { value: serviceMap.value === true, source: serviceMap.source }
|
|
127
|
+
return out
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
module.exports = { cleanLayer, layerFromEnv, layerFromBlock, exportLayer, applyLayer, sources, report }
|
package/package.json
CHANGED
package/preload.mjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// ES module preload, injected with `--import` only when ESM instrumentation (tracing) is
|
|
4
4
|
// enabled (02 D-05, 80 INJ-010). It shares state with preload.cjs (which Node runs first),
|
|
5
5
|
// activates on its own when used alone, and registers the module customization hooks that wrap
|
|
6
|
-
// ES-module libraries when tracing is on (NIO-085, NIO-210). It never
|
|
6
|
+
// ES-module libraries when tracing or the call map is on (NIO-085, NIO-210, 83 CMP-003). It never
|
|
7
|
+
// registers a hook otherwise.
|
|
7
8
|
|
|
8
9
|
import { createRequire } from 'node:module'
|
|
9
10
|
|