diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index e9ac279cc62917..f82a344b42d841 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -1153,6 +1153,20 @@ passed to `console.warn()`. Emitted when `console.error()` is called. Receives and array of the arguments passed to `console.error()`. +#### Diagnotics Channel + +> Stability: 1 - Experimental + + + +##### Event: `'diagnostics_channel.subscribe'` + +* `args` {any\[]} + +Emitted when `diagnostics_channel.subcribe()` or `channel.subscribe()` is called. Receives an object with the channel that was subscribed to, and the subscription function. + #### HTTP > Stability: 1 - Experimental diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js index 3deb301e7f3cd2..f04beda992d62a 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -33,6 +33,10 @@ const { triggerUncaughtException } = internalBinding('errors'); const { WeakReference } = require('internal/util'); +const dcSubscribeChannel = dc.channel('diagnostics_channel.subscribe'); +const dcUnsubscribeChannel = dc.channel('diagnostics_channel.unsubscribe'); +const dcPublishChannel = dc.channel('diagnostics_channel.publish'); + // Can't delete when weakref count reaches 0 as it could increment again. // Only GC can be used as a valid time to clean up the channels map. class WeakRefMap extends SafeMap { @@ -105,12 +109,21 @@ function wrapStoreRun(store, data, next, transform = defaultTransform) { class ActiveChannel { subscribe(subscription) { validateFunction(subscription, 'subscription'); + + if (dcSubscribeChannel.hasSubscribers) { + dcSubscribeChannel.publish({ channel: this, subscription }); + } + this._subscribers = ArrayPrototypeSlice(this._subscribers); ArrayPrototypePush(this._subscribers, subscription); channels.incRef(this.name); } unsubscribe(subscription) { + if (dcUnsubscribeChannel.hasSubscribers) { + dcUnsubscribeChannel.publish({ channel: this, subscription }); + } + const index = ArrayPrototypeIndexOf(this._subscribers, subscription); if (index === -1) return false; @@ -148,7 +161,7 @@ class ActiveChannel { return true; } - publish(data) { + _publish(data) { const subscribers = this._subscribers; for (let i = 0; i < (subscribers?.length || 0); i++) { try { @@ -162,6 +175,14 @@ class ActiveChannel { } } + publish(data) { + if (dcPublishChannel.hasSubscribers) { + dcPublishChannel._publish({ channel: this, data }); + } + + this._publish(data); + } + runStores(data, fn, thisArg, ...args) { let run = () => { this.publish(data); @@ -199,6 +220,9 @@ class Channel { } unsubscribe() { + if (dcUnsubscribeChannel.hasSubscribers) { + dcUnsubscribeChannel.publish({ channel: this, subscription }); + } return false; } @@ -215,7 +239,11 @@ class Channel { return false; } - publish() {} + publish(data) { + if (dcUnsubscribeChannel.hasSubscribers) { + dcUnsubscribeChannel.publish({ channel: this, subscription }); + } + } runStores(data, fn, thisArg, ...args) { return ReflectApply(fn, thisArg, args); diff --git a/test/parallel/test-diagnostics-channel-meta-channels.js b/test/parallel/test-diagnostics-channel-meta-channels.js new file mode 100644 index 00000000000000..55dd5caebdd2d8 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-meta-channels.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const testedChannel = dc.channel('test'); +const testedSubscription = () => {}; +const testedData = { foo: 'bar' }; + +// should publish on meta channel for subscribe() on both inactive and active prototype +dc.subscribe('diagnostics_channel.subscribe', common.mustCall(({ channel, subscription }) => { + assert.strictEqual(channel, testedChannel); + assert.strictEqual(subscription, testedSubscription); +}, 2)); // called twice +testedChannel.subscribe(testedSubscription); // inactive prototype +testedChannel.subscribe(testedSubscription); // active prototype + +// should publish on meta channel for publish() +dc.subscribe('diagnostics_channel.publish', common.mustCall(({ channel, data }) => { + assert.strictEqual(channel, testedChannel); + assert.strictEqual(data, testedData); +})); +testedChannel.publish(testedData); + +// should publish on meta channel for unsubscribe() on both inactive and active prototype +dc.subscribe('diagnostics_channel.unsubscribe', common.mustCall(({ channel, subscription }) => { + assert.strictEqual(channel, testedChannel); + assert.strictEqual(subscription, testedSubscription); +}, 2)); // called twice +testedChannel.unsubscribe(testedSubscription); // active prototype +testedChannel.unsubscribe(testedSubscription); // inactive prototype + + +// TODO: should it publish on inactive channels ?