| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 66ad4be commit b38a43d
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,29 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common.js'); | ||
| 3 | + const dc = require('diagnostics_channel'); | ||
| 4 | + | ||
| 5 | + const bench = common.createBenchmark(main, { | ||
| 6 | + n: [1e8], | ||
| 7 | + subscribers: [0, 1, 10], | ||
| 8 | + }); | ||
| 9 | + | ||
| 10 | + function noop() {} | ||
| 11 | + | ||
| 12 | + function main({ n, subscribers }) { | ||
| 13 | + const channel = dc.channel('test'); | ||
| 14 | + for (let i = 0; i < subscribers; i++) { | ||
| 15 | + channel.subscribe(noop); | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + const data = { | ||
| 19 | + foo: 'bar' | ||
| 20 | + }; | ||
| 21 | + | ||
| 22 | + bench.start(); | ||
| 23 | + for (let i = 0; i < n; i++) { | ||
| 24 | + if (channel.hasSubscribers) { | ||
| 25 | + channel.publish(data); | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + bench.end(n); | ||
| 29 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,19 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common.js'); | ||
| 3 | + const dc = require('diagnostics_channel'); | ||
| 4 | + | ||
| 5 | + const bench = common.createBenchmark(main, { | ||
| 6 | + n: [1e8], | ||
| 7 | + }); | ||
| 8 | + | ||
| 9 | + function noop() {} | ||
| 10 | + | ||
| 11 | + function main({ n }) { | ||
| 12 | + const channel = dc.channel('channel.0'); | ||
| 13 | + | ||
| 14 | + bench.start(); | ||
| 15 | + for (let i = 0; i < n; i++) { | ||
| 16 | + channel.subscribe(noop); | ||
| 17 | + } | ||
| 18 | + bench.end(n); | ||
| 19 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,180 @@ | |||
| 1 | + # Diagnostics Channel | ||
| 2 | + | ||
| 3 | + <!--introduced_in=REPLACEME--> | ||
| 4 | + | ||
| 5 | + > Stability: 1 - Experimental | ||
| 6 | + | ||
| 7 | + <!-- source_link=lib/diagnostics_channel.js --> | ||
| 8 | + | ||
| 9 | + The `diagnostics_channel` module provides an API to create named channels | ||
| 10 | + to report arbitrary message data for diagnostics purposes. | ||
| 11 | + | ||
| 12 | + It can be accessed using: | ||
| 13 | + | ||
| 14 | + ```js | ||
| 15 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 16 | + ``` | ||
| 17 | + | ||
| 18 | + It is intended that a module writer wanting to report diagnostics messages | ||
| 19 | + will create one or many top-level channels to report messages through. | ||
| 20 | + Channels may also be acquired at runtime but it is not encouraged | ||
| 21 | + due to the additional overhead of doing so. Channels may be exported for | ||
| 22 | + convenience, but as long as the name is known it can be acquired anywhere. | ||
| 23 | + | ||
| 24 | + If you intend for your module to produce diagnostics data for others to | ||
| 25 | + consume it is recommended that you include documentation of what named | ||
| 26 | + channels are used along with the shape of the message data. Channel names | ||
| 27 | + should generally include the module name to avoid collisions with data from | ||
| 28 | + other modules. | ||
| 29 | + | ||
| 30 | + ## Public API | ||
| 31 | + | ||
| 32 | + ### Overview | ||
| 33 | + | ||
| 34 | + Following is a simple overview of the public API. | ||
| 35 | + | ||
| 36 | + ```js | ||
| 37 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 38 | + | ||
| 39 | + // Get a reusable channel object | ||
| 40 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 41 | + | ||
| 42 | + // Subscribe to the channel | ||
| 43 | + channel.subscribe((message, name) => { | ||
| 44 | + // Received data | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + // Check if the channel has an active subscriber | ||
| 48 | + if (channel.hasSubscribers) { | ||
| 49 | + // Publish data to the channel | ||
| 50 | + channel.publish({ | ||
| 51 | + some: 'data' | ||
| 52 | + }); | ||
| 53 | + } | ||
| 54 | + ``` | ||
| 55 | + | ||
| 56 | + #### `diagnostics_channel.hasSubscribers(name)` | ||
| 57 | + | ||
| 58 | + * `name` {string|symbol} The channel name | ||
| 59 | + * Returns: {boolean} If there are active subscribers | ||
| 60 | + | ||
| 61 | + Check if there are active subscribers to the named channel. This is helpful if | ||
| 62 | + the message you want to send might be expensive to prepare. | ||
| 63 | + | ||
| 64 | + This API is optional but helpful when trying to publish messages from very | ||
| 65 | + performance-senstive code. | ||
| 66 | + | ||
| 67 | + ```js | ||
| 68 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 69 | + | ||
| 70 | + if (diagnostics_channel.hasSubscribers('my-channel')) { | ||
| 71 | + // There are subscribers, prepare and publish message | ||
| 72 | + } | ||
| 73 | + ``` | ||
| 74 | + | ||
| 75 | + #### `diagnostics_channel.channel(name)` | ||
| 76 | + | ||
| 77 | + * `name` {string|symbol} The channel name | ||
| 78 | + * Returns: {Channel} The named channel object | ||
| 79 | + | ||
| 80 | + This is the primary entry-point for anyone wanting to interact with a named | ||
| 81 | + channel. It produces a channel object which is optimized to reduce overhead at | ||
| 82 | + publish time as much as possible. | ||
| 83 | + | ||
| 84 | + ```js | ||
| 85 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 86 | + | ||
| 87 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 88 | + ``` | ||
| 89 | + | ||
| 90 | + ### Class: `Channel` | ||
| 91 | + | ||
| 92 | + The class `Channel` represents an individual named channel within the data | ||
| 93 | + pipeline. It is use to track subscribers and to publish messages when there | ||
| 94 | + are subscribers present. It exists as a separate object to avoid channel | ||
| 95 | + lookups at publish time, enabling very fast publish speeds and allowing | ||
| 96 | + for heavy use while incurring very minimal cost. Channels are created with | ||
| 97 | + [`diagnostics_channel.channel(name)`][], constructing a channel directly | ||
| 98 | + with `new Channel(name)` is not supported. | ||
| 99 | + | ||
| 100 | + #### `channel.hasSubscribers` | ||
| 101 | + | ||
| 102 | + * Returns: {boolean} If there are active subscribers | ||
| 103 | + | ||
| 104 | + Check if there are active subscribers to this channel. This is helpful if | ||
| 105 | + the message you want to send might be expensive to prepare. | ||
| 106 | + | ||
| 107 | + This API is optional but helpful when trying to publish messages from very | ||
| 108 | + performance-senstive code. | ||
| 109 | + | ||
| 110 | + ```js | ||
| 111 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 112 | + | ||
| 113 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 114 | + | ||
| 115 | + if (channel.hasSubscribers) { | ||
| 116 | + // There are subscribers, prepare and publish message | ||
| 117 | + } | ||
| 118 | + ``` | ||
| 119 | + | ||
| 120 | + #### `channel.publish(message)` | ||
| 121 | + | ||
| 122 | + * `message` {any} The message to send to the channel subscribers | ||
| 123 | + | ||
| 124 | + Publish a message to any subscribers to the channel. This will trigger | ||
| 125 | + message handlers synchronously so they will execute within the same context. | ||
| 126 | + | ||
| 127 | + ```js | ||
| 128 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 129 | + | ||
| 130 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 131 | + | ||
| 132 | + channel.publish({ | ||
| 133 | + some: 'message' | ||
| 134 | + }); | ||
| 135 | + ``` | ||
| 136 | + | ||
| 137 | + #### `channel.subscribe(onMessage)` | ||
| 138 | + | ||
| 139 | + * `onMessage` {Function} The handler to receive channel messages | ||
| 140 | + * `message` {any} The message data | ||
| 141 | + * `name` {string|symbol} The name of the channel | ||
| 142 | + | ||
| 143 | + Register a message handler to subscribe to this channel. This message handler | ||
| 144 | + will be run synchronously whenever a message is published to the channel. Any | ||
| 145 | + errors thrown in the message handler will trigger an [`'uncaughtException'`][]. | ||
| 146 | + | ||
| 147 | + ```js | ||
| 148 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 149 | + | ||
| 150 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 151 | + | ||
| 152 | + channel.subscribe((message, name) => { | ||
| 153 | + // Received data | ||
| 154 | + }); | ||
| 155 | + ``` | ||
| 156 | + | ||
| 157 | + #### `channel.unsubscribe(onMessage)` | ||
| 158 | + | ||
| 159 | + * `onMessage` {Function} The previous subscribed handler to remove | ||
| 160 | + | ||
| 161 | + Remove a message handler previously registered to this channel with | ||
| 162 | + [`channel.subscribe(onMessage)`][]. | ||
| 163 | + | ||
| 164 | + ```js | ||
| 165 | + const diagnostics_channel = require('diagnostics_channel'); | ||
| 166 | + | ||
| 167 | + const channel = diagnostics_channel.channel('my-channel'); | ||
| 168 | + | ||
| 169 | + function onMessage(message, name) { | ||
| 170 | + // Received data | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + channel.subscribe(onMessage); | ||
| 174 | + | ||
| 175 | + channel.unsubscribe(onMessage); | ||
| 176 | + ``` | ||
| 177 | + | ||
| 178 | + [`diagnostics_channel.channel(name)`]: #diagnostics_channel_diagnostics_channel_channel_name | ||
| 179 | + [`channel.subscribe(onMessage)`]: #diagnostics_channel_channel_subscribe_onmessage | ||
| 180 | + [`'uncaughtException'`]: process.md#process_event_uncaughtexception | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,7 @@ | |||
| 23 | 23 | * [Crypto](crypto.md) | |
| 24 | 24 | * [Debugger](debugger.md) | |
| 25 | 25 | * [Deprecated APIs](deprecations.md) | |
| 26 | + * [Diagnostics Channel](diagnostics_channel.md) | ||
| 26 | 27 | * [DNS](dns.md) | |
| 27 | 28 | * [Domain](domain.md) | |
| 28 | 29 | * [Errors](errors.md) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,122 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const { | ||
| 4 | + ArrayPrototypeIndexOf, | ||
| 5 | + ArrayPrototypePush, | ||
| 6 | + ArrayPrototypeSplice, | ||
| 7 | + ObjectCreate, | ||
| 8 | + ObjectGetPrototypeOf, | ||
| 9 | + ObjectSetPrototypeOf, | ||
| 10 | + SymbolHasInstance, | ||
| 11 | + WeakRefPrototypeGet | ||
| 12 | + } = primordials; | ||
| 13 | + | ||
| 14 | + const { | ||
| 15 | + codes: { | ||
| 16 | + ERR_INVALID_ARG_TYPE, | ||
| 17 | + } | ||
| 18 | + } = require('internal/errors'); | ||
| 19 | + | ||
| 20 | + const { triggerUncaughtException } = internalBinding('errors'); | ||
| 21 | + | ||
| 22 | + const { WeakReference } = internalBinding('util'); | ||
| 23 | + | ||
| 24 | + // TODO(qard): should there be a C++ channel interface? | ||
| 25 | + class ActiveChannel { | ||
| 26 | + subscribe(subscription) { | ||
| 27 | + if (typeof subscription !== 'function') { | ||
| 28 | + throw new ERR_INVALID_ARG_TYPE('subscription', ['function'], | ||
| 29 | + subscription); | ||
| 30 | + } | ||
| 31 | + ArrayPrototypePush(this._subscribers, subscription); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + unsubscribe(subscription) { | ||
| 35 | + const index = ArrayPrototypeIndexOf(this._subscribers, subscription); | ||
| 36 | + if (index >= 0) { | ||
| 37 | + ArrayPrototypeSplice(this._subscribers, index, 1); | ||
| 38 | + | ||
| 39 | + // When there are no more active subscribers, restore to fast prototype. | ||
| 40 | + if (!this._subscribers.length) { | ||
| 41 | + // eslint-disable-next-line no-use-before-define | ||
| 42 | + ObjectSetPrototypeOf(this, Channel.prototype); | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + get hasSubscribers() { | ||
| 48 | + return true; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + publish(data) { | ||
| 52 | + for (let i = 0; i < this._subscribers.length; i++) { | ||
| 53 | + try { | ||
| 54 | + const onMessage = this._subscribers[i]; | ||
| 55 | + onMessage(data, this.name); | ||
| 56 | + } catch (err) { | ||
| 57 | + process.nextTick(() => { | ||
| 58 | + triggerUncaughtException(err, false); | ||
| 59 | + }); | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + class Channel { | ||
| 66 | + constructor(name) { | ||
| 67 | + this._subscribers = undefined; | ||
| 68 | + this.name = name; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + static [SymbolHasInstance](instance) { | ||
| 72 | + const prototype = ObjectGetPrototypeOf(instance); | ||
| 73 | + return prototype === Channel.prototype || | ||
| 74 | + prototype === ActiveChannel.prototype; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + subscribe(subscription) { | ||
| 78 | + ObjectSetPrototypeOf(this, ActiveChannel.prototype); | ||
| 79 | + this._subscribers = []; | ||
| 80 | + this.subscribe(subscription); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + get hasSubscribers() { | ||
| 84 | + return false; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + publish() {} | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + const channels = ObjectCreate(null); | ||
| 91 | + | ||
| 92 | + function channel(name) { | ||
| 93 | + let channel; | ||
| 94 | + const ref = channels[name]; | ||
| 95 | + if (ref) channel = ref.get(); | ||
| 96 | + if (channel) return channel; | ||
| 97 | + | ||
| 98 | + if (typeof name !== 'string' && typeof name !== 'symbol') { | ||
| 99 | + throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + channel = new Channel(name); | ||
| 103 | + channels[name] = new WeakReference(channel); | ||
| 104 | + return channel; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + function hasSubscribers(name) { | ||
| 108 | + let channel; | ||
| 109 | + const ref = channels[name]; | ||
| 110 | + if (ref) channel = WeakRefPrototypeGet(ref); | ||
| 111 | + if (!channel) { | ||
| 112 | + return false; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + return channel.hasSubscribers; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + module.exports = { | ||
| 119 | + channel, | ||
| 120 | + hasSubscribers, | ||
| 121 | + Channel | ||
| 122 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,6 +51,7 @@ | |||
| 51 | 51 | 'lib/constants.js', | |
| 52 | 52 | 'lib/crypto.js', | |
| 53 | 53 | 'lib/cluster.js', | |
| 54 | + 'lib/diagnostics_channel.js', | ||
| 54 | 55 | 'lib/dgram.js', | |
| 55 | 56 | 'lib/dns.js', | |
| 56 | 57 | 'lib/dns/promises.js', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments