Skip to content

Commit f91a17e

Browse files
committed
ref(node): Refactor node integrations to use processEvent
1 parent 94c68e0 commit f91a17e

File tree

6 files changed

+179
-140
lines changed

6 files changed

+179
-140
lines changed

packages/node/src/integrations/context.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import type {
66
CultureContext,
77
DeviceContext,
88
Event,
9-
EventProcessor,
109
Integration,
1110
OsContext,
1211
} from '@sentry/types';
@@ -63,17 +62,26 @@ export class Context implements Integration {
6362
/**
6463
* @inheritDoc
6564
*/
66-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void): void {
67-
addGlobalEventProcessor(event => this.addContext(event));
65+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
66+
// noop
67+
}
68+
69+
/** @inheritDoc */
70+
public processEvent(event: Event): Promise<Event> {
71+
return this.addContext(event);
6872
}
6973

70-
/** Processes an event and adds context */
74+
/**
75+
* Processes an event and adds context.
76+
*
77+
* TODO (v8): Make this private/internal.
78+
*/
7179
public async addContext(event: Event): Promise<Event> {
7280
if (this._cachedContext === undefined) {
7381
this._cachedContext = this._getContexts();
7482
}
7583

76-
const updatedContext = this._updateContext(await this._cachedContext);
84+
const updatedContext = _updateContext(await this._cachedContext);
7785

7886
event.contexts = {
7987
...event.contexts,
@@ -87,22 +95,6 @@ export class Context implements Integration {
8795
return event;
8896
}
8997

90-
/**
91-
* Updates the context with dynamic values that can change
92-
*/
93-
private _updateContext(contexts: Contexts): Contexts {
94-
// Only update properties if they exist
95-
if (contexts?.app?.app_memory) {
96-
contexts.app.app_memory = process.memoryUsage().rss;
97-
}
98-
99-
if (contexts?.device?.free_memory) {
100-
contexts.device.free_memory = os.freemem();
101-
}
102-
103-
return contexts;
104-
}
105-
10698
/**
10799
* Gets the contexts for the current environment
108100
*/
@@ -137,6 +129,22 @@ export class Context implements Integration {
137129
}
138130
}
139131

132+
/**
133+
* Updates the context with dynamic values that can change
134+
*/
135+
function _updateContext(contexts: Contexts): Contexts {
136+
// Only update properties if they exist
137+
if (contexts?.app?.app_memory) {
138+
contexts.app.app_memory = process.memoryUsage().rss;
139+
}
140+
141+
if (contexts?.device?.free_memory) {
142+
contexts.device.free_memory = os.freemem();
143+
}
144+
145+
return contexts;
146+
}
147+
140148
/**
141149
* Returns the operating system context.
142150
*

packages/node/src/integrations/contextlines.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types';
1+
import type { Event, Integration, StackFrame } from '@sentry/types';
22
import { addContextToFrame } from '@sentry/utils';
33
import { readFile } from 'fs';
44
import { LRUMap } from 'lru_map';
@@ -56,17 +56,20 @@ export class ContextLines implements Integration {
5656
/**
5757
* @inheritDoc
5858
*/
59-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
60-
addGlobalEventProcessor(event => {
61-
const self = getCurrentHub().getIntegration(ContextLines);
62-
if (!self) {
63-
return event;
64-
}
65-
return this.addSourceContext(event);
66-
});
59+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
60+
// noop
6761
}
6862

69-
/** Processes an event and adds context lines */
63+
/** @inheritDoc */
64+
public processEvent(event: Event): Promise<Event> {
65+
return this.addSourceContext(event);
66+
}
67+
68+
/**
69+
* Processes an event and adds context lines.
70+
*
71+
* TODO (v8): Make this internal/private
72+
* */
7073
public async addSourceContext(event: Event): Promise<Event> {
7174
// keep a lookup map of which files we've already enqueued to read,
7275
// so we don't enqueue the same file multiple times which would cause multiple i/o reads
@@ -117,7 +120,11 @@ export class ContextLines implements Integration {
117120
return event;
118121
}
119122

120-
/** Adds context lines to frames */
123+
/**
124+
* Adds context lines to frames.
125+
*
126+
* TODO (v8): Make this internal/private
127+
*/
121128
public addSourceContextToFrames(frames: StackFrame[]): void {
122129
for (const frame of frames) {
123130
// Only add context if we have a filename and it hasn't already been added

packages/node/src/integrations/localvariables.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import type { Event, EventProcessor, Exception, Hub, Integration, StackFrame, StackParser } from '@sentry/types';
1+
import type {
2+
Client,
3+
Event,
4+
EventHint,
5+
EventProcessor,
6+
Exception,
7+
Hub,
8+
Integration,
9+
StackFrame,
10+
StackParser,
11+
} from '@sentry/types';
212
import { logger } from '@sentry/utils';
313
import type { Debugger, InspectorNotification, Runtime, Session } from 'inspector';
414
import { LRUMap } from 'lru_map';
@@ -278,29 +288,39 @@ export class LocalVariables implements Integration {
278288
this._setup(addGlobalEventProcessor, getCurrentHub().getClient()?.getOptions());
279289
}
280290

291+
/** @inheritDoc */
292+
public processEvent(event: Event, hint: EventHint | undefined, client: Client): Event {
293+
const clientOptions = client.getOptions() as NodeClientOptions;
294+
if (!this._session || !clientOptions.includeLocalVariables) {
295+
return event;
296+
}
297+
298+
return this._addLocalVariables(event);
299+
}
300+
281301
/** Setup in a way that's easier to call from tests */
282302
private _setup(
283303
addGlobalEventProcessor: (callback: EventProcessor) => void,
284304
clientOptions: NodeClientOptions | undefined,
285305
): void {
286-
if (this._session && clientOptions?.includeLocalVariables) {
287-
// Only setup this integration if the Node version is >= v18
288-
// https://github.com/getsentry/sentry-javascript/issues/7697
289-
const unsupportedNodeVersion = (NODE_VERSION.major || 0) < 18;
290-
291-
if (unsupportedNodeVersion) {
292-
logger.log('The `LocalVariables` integration is only supported on Node >= v18.');
293-
return;
294-
}
306+
if (!this._session || !clientOptions?.includeLocalVariables) {
307+
return;
308+
}
295309

296-
this._session.configureAndConnect(
297-
(ev, complete) =>
298-
this._handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),
299-
!!this._options.captureAllExceptions,
300-
);
310+
// Only setup this integration if the Node version is >= v18
311+
// https://github.com/getsentry/sentry-javascript/issues/7697
312+
const unsupportedNodeVersion = (NODE_VERSION.major || 0) < 18;
301313

302-
addGlobalEventProcessor(async event => this._addLocalVariables(event));
314+
if (unsupportedNodeVersion) {
315+
logger.log('The `LocalVariables` integration is only supported on Node >= v18.');
316+
return;
303317
}
318+
319+
this._session.configureAndConnect(
320+
(ev, complete) =>
321+
this._handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>, complete),
322+
!!this._options.captureAllExceptions,
323+
);
304324
}
305325

306326
/**

packages/node/src/integrations/modules.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { EventProcessor, Hub, Integration } from '@sentry/types';
1+
import type { Event, Integration } from '@sentry/types';
22
import { existsSync, readFileSync } from 'fs';
33
import { dirname, join } from 'path';
44

@@ -80,26 +80,26 @@ export class Modules implements Integration {
8080
/**
8181
* @inheritDoc
8282
*/
83-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
84-
addGlobalEventProcessor(event => {
85-
if (!getCurrentHub().getIntegration(Modules)) {
86-
return event;
87-
}
88-
return {
89-
...event,
90-
modules: {
91-
...event.modules,
92-
...this._getModules(),
93-
},
94-
};
95-
});
83+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
84+
// noop
9685
}
9786

98-
/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */
99-
private _getModules(): { [key: string]: string } {
100-
if (!moduleCache) {
101-
moduleCache = collectModules();
102-
}
103-
return moduleCache;
87+
/** @inheritDoc */
88+
public processEvent(event: Event): Event {
89+
return {
90+
...event,
91+
modules: {
92+
...event.modules,
93+
..._getModules(),
94+
},
95+
};
96+
}
97+
}
98+
99+
/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */
100+
function _getModules(): { [key: string]: string } {
101+
if (!moduleCache) {
102+
moduleCache = collectModules();
104103
}
104+
return moduleCache;
105105
}

packages/node/src/integrations/requestdata.ts

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// TODO (v8 or v9): Whenever this becomes a default integration for `@sentry/browser`, move this to `@sentry/core`. For
22
// now, we leave it in `@sentry/integrations` so that it doesn't contribute bytes to our CDN bundles.
33

4-
import type { Event, EventProcessor, Hub, Integration, PolymorphicRequest, Transaction } from '@sentry/types';
4+
import type { Client, Event, EventHint, Integration, PolymorphicRequest, Transaction } from '@sentry/types';
55
import { extractPathForTransaction } from '@sentry/utils';
66

77
import type { AddRequestDataToEventOptions, TransactionNamingScheme } from '../requestdata';
@@ -99,65 +99,66 @@ export class RequestData implements Integration {
9999
/**
100100
* @inheritDoc
101101
*/
102-
public setupOnce(addGlobalEventProcessor: (eventProcessor: EventProcessor) => void, getCurrentHub: () => Hub): void {
102+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
103+
// noop
104+
}
105+
106+
/** @inheritDoc */
107+
public processEvent(event: Event, hint: EventHint | undefined, client: Client): Event {
103108
// Note: In the long run, most of the logic here should probably move into the request data utility functions. For
104109
// the moment it lives here, though, until https://github.com/getsentry/sentry-javascript/issues/5718 is addressed.
105110
// (TL;DR: Those functions touch many parts of the repo in many different ways, and need to be clened up. Once
106111
// that's happened, it will be easier to add this logic in without worrying about unexpected side effects.)
107-
const { transactionNamingScheme } = this._options;
108112

109-
addGlobalEventProcessor(event => {
110-
const hub = getCurrentHub();
111-
const self = hub.getIntegration(RequestData);
113+
const { transactionNamingScheme } = this._options;
112114

113-
const { sdkProcessingMetadata = {} } = event;
114-
const req = sdkProcessingMetadata.request;
115+
const { sdkProcessingMetadata = {} } = event;
116+
const req = sdkProcessingMetadata.request;
115117

116-
// If the globally installed instance of this integration isn't associated with the current hub, `self` will be
117-
// undefined
118-
if (!self || !req) {
119-
return event;
120-
}
118+
// If the globally installed instance of this integration isn't associated with the current hub, `self` will be
119+
// undefined
120+
if (!req) {
121+
return event;
122+
}
121123

122-
// The Express request handler takes a similar `include` option to that which can be passed to this integration.
123-
// If passed there, we store it in `sdkProcessingMetadata`. TODO(v8): Force express and GCP people to use this
124-
// integration, so that all of this passing and conversion isn't necessary
125-
const addRequestDataOptions =
126-
sdkProcessingMetadata.requestDataOptionsFromExpressHandler ||
127-
sdkProcessingMetadata.requestDataOptionsFromGCPWrapper ||
128-
convertReqDataIntegrationOptsToAddReqDataOpts(this._options);
124+
// The Express request handler takes a similar `include` option to that which can be passed to this integration.
125+
// If passed there, we store it in `sdkProcessingMetadata`. TODO(v8): Force express and GCP people to use this
126+
// integration, so that all of this passing and conversion isn't necessary
127+
const addRequestDataOptions =
128+
sdkProcessingMetadata.requestDataOptionsFromExpressHandler ||
129+
sdkProcessingMetadata.requestDataOptionsFromGCPWrapper ||
130+
convertReqDataIntegrationOptsToAddReqDataOpts(this._options);
129131

130-
const processedEvent = this._addRequestData(event, req, addRequestDataOptions);
132+
const processedEvent = this._addRequestData(event, req, addRequestDataOptions);
131133

132-
// Transaction events already have the right `transaction` value
133-
if (event.type === 'transaction' || transactionNamingScheme === 'handler') {
134-
return processedEvent;
135-
}
134+
// Transaction events already have the right `transaction` value
135+
if (event.type === 'transaction' || transactionNamingScheme === 'handler') {
136+
return processedEvent;
137+
}
136138

137-
// In all other cases, use the request's associated transaction (if any) to overwrite the event's `transaction`
138-
// value with a high-quality one
139-
const reqWithTransaction = req as { _sentryTransaction?: Transaction };
140-
const transaction = reqWithTransaction._sentryTransaction;
141-
if (transaction) {
142-
// TODO (v8): Remove the nextjs check and just base it on `transactionNamingScheme` for all SDKs. (We have to
143-
// keep it the way it is for the moment, because changing the names of transactions in Sentry has the potential
144-
// to break things like alert rules.)
145-
const shouldIncludeMethodInTransactionName =
146-
getSDKName(hub) === 'sentry.javascript.nextjs'
147-
? transaction.name.startsWith('/api')
148-
: transactionNamingScheme !== 'path';
149-
150-
const [transactionValue] = extractPathForTransaction(req, {
151-
path: true,
152-
method: shouldIncludeMethodInTransactionName,
153-
customRoute: transaction.name,
154-
});
155-
156-
processedEvent.transaction = transactionValue;
157-
}
139+
// In all other cases, use the request's associated transaction (if any) to overwrite the event's `transaction`
140+
// value with a high-quality one
141+
const reqWithTransaction = req as { _sentryTransaction?: Transaction };
142+
const transaction = reqWithTransaction._sentryTransaction;
143+
if (transaction) {
144+
// TODO (v8): Remove the nextjs check and just base it on `transactionNamingScheme` for all SDKs. (We have to
145+
// keep it the way it is for the moment, because changing the names of transactions in Sentry has the potential
146+
// to break things like alert rules.)
147+
const shouldIncludeMethodInTransactionName =
148+
getSDKName(client) === 'sentry.javascript.nextjs'
149+
? transaction.name.startsWith('/api')
150+
: transactionNamingScheme !== 'path';
151+
152+
const [transactionValue] = extractPathForTransaction(req, {
153+
path: true,
154+
method: shouldIncludeMethodInTransactionName,
155+
customRoute: transaction.name,
156+
});
157+
158+
processedEvent.transaction = transactionValue;
159+
}
158160

159-
return processedEvent;
160-
});
161+
return processedEvent;
161162
}
162163
}
163164

@@ -203,12 +204,12 @@ function convertReqDataIntegrationOptsToAddReqDataOpts(
203204
};
204205
}
205206

206-
function getSDKName(hub: Hub): string | undefined {
207+
function getSDKName(client: Client): string | undefined {
207208
try {
208209
// For a long chain like this, it's fewer bytes to combine a try-catch with assuming everything is there than to
209210
// write out a long chain of `a && a.b && a.b.c && ...`
210211
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
211-
return hub.getClient()!.getOptions()!._metadata!.sdk!.name;
212+
return client.getOptions()!._metadata!.sdk!.name;
212213
} catch (err) {
213214
// In theory we should never get here
214215
return undefined;

0 commit comments

Comments
 (0)