Skip to content

Commit cf36d41

Browse files
committed
feat(node): Undici integration
1 parent 1418582 commit cf36d41

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dist/
1111
coverage/
1212
scratch/
1313
*.d.ts
14+
!diagnostics_channel.d.ts
1415
*.js.map
1516
*.pyc
1617
*.tsbuildinfo
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Vendored from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fc6d58e6434810867a9483e2107ea51bfca9153/types/node/diagnostics_channel.d.ts
2+
3+
// License:
4+
// This project is licensed under the MIT license.
5+
// Copyrights are respective of each contributor listed at the beginning of each definition file.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8+
// documentation files(the "Software"), to deal in the Software without restriction, including without limitation the
9+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to
10+
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS
16+
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18+
19+
// Vendored code starts here:
20+
/**
21+
* The `diagnostics_channel` module provides an API to create named channels
22+
* to report arbitrary message data for diagnostics purposes.
23+
*
24+
* It can be accessed using:
25+
*
26+
* ```js
27+
* import diagnostics_channel from 'diagnostics_channel';
28+
* ```
29+
*
30+
* It is intended that a module writer wanting to report diagnostics messages
31+
* will create one or many top-level channels to report messages through.
32+
* Channels may also be acquired at runtime but it is not encouraged
33+
* due to the additional overhead of doing so. Channels may be exported for
34+
* convenience, but as long as the name is known it can be acquired anywhere.
35+
*
36+
* If you intend for your module to produce diagnostics data for others to
37+
* consume it is recommended that you include documentation of what named
38+
* channels are used along with the shape of the message data. Channel names
39+
* should generally include the module name to avoid collisions with data from
40+
* other modules.
41+
* @experimental
42+
* @see [source](https://github.com/nodejs/node/blob/v18.0.0/lib/diagnostics_channel.js)
43+
*/
44+
declare module 'diagnostics_channel' {
45+
/**
46+
* Check if there are active subscribers to the named channel. This is helpful if
47+
* the message you want to send might be expensive to prepare.
48+
*
49+
* This API is optional but helpful when trying to publish messages from very
50+
* performance-sensitive code.
51+
*
52+
* ```js
53+
* import diagnostics_channel from 'diagnostics_channel';
54+
*
55+
* if (diagnostics_channel.hasSubscribers('my-channel')) {
56+
* // There are subscribers, prepare and publish message
57+
* }
58+
* ```
59+
* @since v15.1.0, v14.17.0
60+
* @param name The channel name
61+
* @return If there are active subscribers
62+
*/
63+
function hasSubscribers(name: string | symbol): boolean;
64+
/**
65+
* This is the primary entry-point for anyone wanting to interact with a named
66+
* channel. It produces a channel object which is optimized to reduce overhead at
67+
* publish time as much as possible.
68+
*
69+
* ```js
70+
* import diagnostics_channel from 'diagnostics_channel';
71+
*
72+
* const channel = diagnostics_channel.channel('my-channel');
73+
* ```
74+
* @since v15.1.0, v14.17.0
75+
* @param name The channel name
76+
* @return The named channel object
77+
*/
78+
function channel(name: string | symbol): Channel;
79+
type ChannelListener = (message: unknown, name: string | symbol) => void;
80+
/**
81+
* The class `Channel` represents an individual named channel within the data
82+
* pipeline. It is use to track subscribers and to publish messages when there
83+
* are subscribers present. It exists as a separate object to avoid channel
84+
* lookups at publish time, enabling very fast publish speeds and allowing
85+
* for heavy use while incurring very minimal cost. Channels are created with {@link channel}, constructing a channel directly
86+
* with `new Channel(name)` is not supported.
87+
* @since v15.1.0, v14.17.0
88+
*/
89+
class Channel {
90+
public readonly name: string | symbol;
91+
/**
92+
* Check if there are active subscribers to this channel. This is helpful if
93+
* the message you want to send might be expensive to prepare.
94+
*
95+
* This API is optional but helpful when trying to publish messages from very
96+
* performance-sensitive code.
97+
*
98+
* ```js
99+
* import diagnostics_channel from 'diagnostics_channel';
100+
*
101+
* const channel = diagnostics_channel.channel('my-channel');
102+
*
103+
* if (channel.hasSubscribers) {
104+
* // There are subscribers, prepare and publish message
105+
* }
106+
* ```
107+
* @since v15.1.0, v14.17.0
108+
*/
109+
public readonly hasSubscribers: boolean;
110+
private constructor(name: string | symbol);
111+
/**
112+
* Publish a message to any subscribers to the channel. This will
113+
* trigger message handlers synchronously so they will execute within
114+
* the same context.
115+
*
116+
* ```js
117+
* import diagnostics_channel from 'diagnostics_channel';
118+
*
119+
* const channel = diagnostics_channel.channel('my-channel');
120+
*
121+
* channel.publish({
122+
* some: 'message'
123+
* });
124+
* ```
125+
* @since v15.1.0, v14.17.0
126+
* @param message The message to send to the channel subscribers
127+
*/
128+
public publish(message: unknown): void;
129+
/**
130+
* Register a message handler to subscribe to this channel. This message handler
131+
* will be run synchronously whenever a message is published to the channel. Any
132+
* errors thrown in the message handler will trigger an `'uncaughtException'`.
133+
*
134+
* ```js
135+
* import diagnostics_channel from 'diagnostics_channel';
136+
*
137+
* const channel = diagnostics_channel.channel('my-channel');
138+
*
139+
* channel.subscribe((message, name) => {
140+
* // Received data
141+
* });
142+
* ```
143+
* @since v15.1.0, v14.17.0
144+
* @param onMessage The handler to receive channel messages
145+
*/
146+
public subscribe(onMessage: ChannelListener): void;
147+
/**
148+
* Remove a message handler previously registered to this channel with `channel.subscribe(onMessage)`.
149+
*
150+
* ```js
151+
* import diagnostics_channel from 'diagnostics_channel';
152+
*
153+
* const channel = diagnostics_channel.channel('my-channel');
154+
*
155+
* function onMessage(message, name) {
156+
* // Received data
157+
* }
158+
*
159+
* channel.subscribe(onMessage);
160+
*
161+
* channel.unsubscribe(onMessage);
162+
* ```
163+
* @since v15.1.0, v14.17.0
164+
* @param onMessage The previous subscribed handler to remove
165+
* @return `true` if the handler was found, `false` otherwise.
166+
*/
167+
public unsubscribe(onMessage: ChannelListener): void;
168+
}
169+
}
170+
declare module 'node:diagnostics_channel' {
171+
export * from 'diagnostics_channel';
172+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { Hub } from '@sentry/core';
2+
import type { EventProcessor, Integration } from '@sentry/types';
3+
import type DiagnosticsChannel from 'diagnostics_channel';
4+
5+
/** */
6+
export class Undici implements Integration {
7+
/**
8+
* @inheritDoc
9+
*/
10+
public static id: string = 'Undici';
11+
12+
/**
13+
* @inheritDoc
14+
*/
15+
public name: string = Undici.id;
16+
17+
// Have to hold all built channels in memory otherwise they get garbage collected
18+
// See: https://github.com/nodejs/node/pull/42714
19+
// This has been fixed in Node 19+
20+
private _channels: Map<string, DiagnosticsChannel.Channel> = new Map();
21+
22+
/**
23+
* @inheritDoc
24+
*/
25+
public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, _getCurrentHub: () => Hub): void {
26+
let ds: typeof DiagnosticsChannel | undefined;
27+
try {
28+
// eslint-disable-next-line @typescript-eslint/no-var-requires
29+
ds = require('diagnostics_channel') as typeof DiagnosticsChannel;
30+
} catch (e) {
31+
// no-op
32+
}
33+
34+
if (!ds) {
35+
return;
36+
}
37+
38+
// https://github.com/nodejs/undici/blob/main/docs/api/DiagnosticsChannel.md
39+
const undiciChannel = ds.channel('undici:request');
40+
}
41+
42+
private _setupChannel(name: Parameters<typeof DiagnosticsChannel.channel>[0]): void {
43+
const channel = DiagnosticsChannel.channel(name);
44+
if (node)
45+
}
46+
}

0 commit comments

Comments
 (0)