diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index 4587ee649b5173..1d822ed4095f4f 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 bd17965b131208..9b2f3702525168 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -36,6 +36,10 @@ const { subscribers: subscriberCounts } = dc_binding; const { WeakReference } = require('internal/util'); +let dcSubscribeChannel; +let dcUnsubscribeChannel; +let dcPublishChannel; + // 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 { @@ -108,6 +112,11 @@ 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); @@ -115,6 +124,10 @@ class ActiveChannel { } unsubscribe(subscription) { + if (dcUnsubscribeChannel.hasSubscribers) { + dcUnsubscribeChannel._publish({ channel: this, subscription }); + } + const index = ArrayPrototypeIndexOf(this._subscribers, subscription); if (index === -1) return false; @@ -157,7 +170,7 @@ class ActiveChannel { return true; } - publish(data) { + _publish(data) { const subscribers = this._subscribers; for (let i = 0; i < (subscribers?.length || 0); i++) { try { @@ -171,6 +184,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); @@ -210,7 +231,10 @@ class Channel { this.subscribe(subscription); } - unsubscribe() { + unsubscribe(subscription) { + if (dcUnsubscribeChannel.hasSubscribers) { + dcUnsubscribeChannel._publish({ channel: this, subscription }); + } return false; } @@ -227,7 +251,11 @@ class Channel { return false; } - publish() {} + publish(data) { + if (dcPublishChannel.hasSubscribers) { + dcPublishChannel._publish({ channel: this, data }); + } + } runStores(data, fn, thisArg, ...args) { return ReflectApply(fn, thisArg, args); @@ -247,6 +275,10 @@ function channel(name) { return new Channel(name); } +dcSubscribeChannel = channel('diagnostics_channel.subscribe'); +dcUnsubscribeChannel = channel('diagnostics_channel.unsubscribe'); +dcPublishChannel = channel('diagnostics_channel.publish'); + function subscribe(name, subscription) { return channel(name).subscribe(subscription); } 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 ?