Skip to content

Commit 8ec9cdf

Browse files
committed
use integration and avoid global module scope
1 parent c9067f3 commit 8ec9cdf

File tree

4 files changed

+60
-62
lines changed

4 files changed

+60
-62
lines changed

packages/node-core/src/integrations/http/httpServerIntegration.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ interface ServerCallbackOptions {
3131
normalizedRequest: RequestEventData;
3232
}
3333

34-
const clientToCallbackMap = new WeakMap<Client, ((fn: () => boolean, options: ServerCallbackOptions) => boolean)[]>();
34+
type ServerCallback = (fn: () => boolean, options: ServerCallbackOptions) => boolean;
3535

3636
const clientToRequestSessionAggregatesMap = new Map<
3737
Client,
@@ -94,18 +94,22 @@ const _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}) =>
9494
ignoreRequestBody: options.ignoreRequestBody,
9595
};
9696

97+
const serverCallbacks: ServerCallback[] = [];
98+
9799
return {
98100
name: INTEGRATION_NAME,
99101
setupOnce() {
100102
const onHttpServerRequestStart = ((_data: unknown) => {
101103
const data = _data as { server: Server };
102104

103-
instrumentServer(data.server, _options);
105+
instrumentServer(data.server, _options, serverCallbacks);
104106
}) satisfies ChannelListener;
105107

106108
subscribe('http.server.request.start', onHttpServerRequestStart);
107109
},
108-
110+
addServerCallback(callback: ServerCallback) {
111+
serverCallbacks.push(callback);
112+
},
109113
afterAllSetup(client) {
110114
if (DEBUG_BUILD && client.getIntegrationByName('Http')) {
111115
debug.warn(
@@ -125,6 +129,7 @@ export const httpServerIntegration = _httpServerIntegration as (
125129
) => Integration & {
126130
name: 'HttpServer';
127131
setupOnce: () => void;
132+
addServerCallback: (callback: ServerCallback) => void;
128133
};
129134

130135
/**
@@ -144,6 +149,7 @@ function instrumentServer(
144149
sessions: boolean;
145150
sessionFlushingDelayMS: number;
146151
},
152+
serverCallbacks: ServerCallback[],
147153
): void {
148154
// eslint-disable-next-line @typescript-eslint/unbound-method
149155
const originalEmit: ServerEmit = server.emit;
@@ -215,13 +221,11 @@ function instrumentServer(
215221
.setValue(HTTP_SERVER_INSTRUMENTED_KEY, true);
216222

217223
return context.with(ctx, () => {
218-
const callbacks = clientToCallbackMap.get(client);
219-
220-
if (callbacks?.length) {
224+
if (serverCallbacks?.length) {
221225
return wrapInCallbacks(
222226
() => target.apply(thisArg, args),
223227
{ request, response, normalizedRequest },
224-
callbacks.slice(),
228+
serverCallbacks.slice(),
225229
);
226230
} else {
227231
return target.apply(thisArg, args);
@@ -235,18 +239,6 @@ function instrumentServer(
235239
server.emit = newEmit;
236240
}
237241

238-
/**
239-
* Register a client callback that will be called when a request is received.
240-
*/
241-
export function registerServerCallback(
242-
client: Client,
243-
callback: (fn: () => boolean, options: ServerCallbackOptions) => boolean,
244-
): void {
245-
const callbacks = clientToCallbackMap.get(client) || [];
246-
callbacks.push(callback);
247-
clientToCallbackMap.set(client, callbacks);
248-
}
249-
250242
/**
251243
* Starts a session and tracks it in the context of a given isolation scope.
252244
* When the passed response is finished, the session is put into a task and is

packages/node-core/src/integrations/http/httpServerSpansIntegration.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
} from '@sentry/core';
3434
import { DEBUG_BUILD } from '../../debug-build';
3535
import type { NodeClient } from '../../sdk/client';
36-
import { registerServerCallback } from './httpServerIntegration';
36+
import type { httpServerIntegration } from './httpServerIntegration';
3737

3838
const INTEGRATION_NAME = 'Http.ServerSpans';
3939

@@ -104,9 +104,36 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
104104
// eslint-disable-next-line deprecation/deprecation
105105
const { requestHook, responseHook, applyCustomAttributesOnSpan } = options.instrumentation ?? {};
106106

107+
// We track if setup() was called, which indicates that this integration was added directly, not called from httpIntegration
108+
let _isSetupDirectly = false;
109+
107110
return {
108111
name: INTEGRATION_NAME,
109-
setup(client: NodeClient) {
112+
setup() {
113+
_isSetupDirectly = true;
114+
},
115+
processEvent(event) {
116+
// Drop transaction if it has a status code that should be ignored
117+
if (event.type === 'transaction') {
118+
const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];
119+
if (typeof statusCode === 'number') {
120+
const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);
121+
if (shouldDrop) {
122+
DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);
123+
return null;
124+
}
125+
}
126+
}
127+
128+
return event;
129+
},
130+
afterAllSetup(client: NodeClient) {
131+
if (DEBUG_BUILD && client.getIntegrationByName('Http') && _isSetupDirectly) {
132+
debug.warn(
133+
'It seems that you have manually added `httpServerSpansIntergation` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',
134+
);
135+
}
136+
110137
// If no tracing, we can just skip everything here
111138
if (typeof __SENTRY_TRACING__ !== 'undefined' && !__SENTRY_TRACING__) {
112139
return;
@@ -222,28 +249,12 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
222249
});
223250
}
224251

225-
registerServerCallback(client, startSpan);
226-
},
227-
processEvent(event) {
228-
// Drop transaction if it has a status code that should be ignored
229-
if (event.type === 'transaction') {
230-
const statusCode = event.contexts?.trace?.data?.['http.response.status_code'];
231-
if (typeof statusCode === 'number') {
232-
const shouldDrop = shouldFilterStatusCode(statusCode, ignoreStatusCodes);
233-
if (shouldDrop) {
234-
DEBUG_BUILD && debug.log('Dropping transaction due to status code', statusCode);
235-
return null;
236-
}
237-
}
238-
}
239-
240-
return event;
241-
},
242-
afterAllSetup(client) {
243-
if (DEBUG_BUILD && client.getIntegrationByName('Http')) {
244-
debug.warn(
245-
'It seems that you have manually added `httpServerSpansIntergation` while `httpIntegration` is also present. Make sure to remove `httpIntegration` when adding `httpServerSpansIntegration`.',
246-
);
252+
const serverIntegration = client.getIntegrationByName<ReturnType<typeof httpServerIntegration>>('HttpServer');
253+
if (serverIntegration) {
254+
serverIntegration.addServerCallback(startSpan);
255+
} else {
256+
DEBUG_BUILD &&
257+
debug.warn('httpServerSpansIntegration requires the httpServerIntegration to be present. Skipping...');
247258
}
248259
},
249260
};
@@ -257,7 +268,7 @@ export const httpServerSpansIntegration = _httpServerSpansIntegration as (
257268
options?: HttpServerSpansIntegrationOptions,
258269
) => Integration & {
259270
name: 'HttpServerSpans';
260-
setup: (client: NodeClient) => void;
271+
afterAllSetup: (client: NodeClient) => void;
261272
processEvent: (event: Event) => Event | null;
262273
};
263274

packages/node-core/src/integrations/http/index.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { IncomingMessage, RequestOptions } from 'node:http';
2-
import { debug, defineIntegration } from '@sentry/core';
3-
import { DEBUG_BUILD } from '../../debug-build';
2+
import { defineIntegration } from '@sentry/core';
43
import { generateInstrumentOnce } from '../../otel/instrument';
54
import type { NodeClient } from '../../sdk/client';
65
import type { HttpServerIntegrationOptions } from './httpServerIntegration';
@@ -157,18 +156,16 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) =>
157156

158157
return {
159158
name: INTEGRATION_NAME,
160-
161-
setup(client: NodeClient) {
162-
if (enabledServerSpans) {
163-
serverSpans.setup(client);
164-
}
165-
},
166-
167159
setupOnce() {
168160
server.setupOnce();
169161

170162
instrumentSentryHttp(httpInstrumentationOptions);
171163
},
164+
afterAllSetup(client: NodeClient) {
165+
if (enabledServerSpans) {
166+
serverSpans.afterAllSetup(client);
167+
}
168+
},
172169
processEvent(event) {
173170
// Note: We always run this, even if spans are disabled
174171
// The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option

packages/node/src/integrations/http.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,15 +226,6 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) =>
226226

227227
return {
228228
name: INTEGRATION_NAME,
229-
230-
setup(client: NodeClient) {
231-
const clientOptions = client.getOptions();
232-
233-
if (enableServerSpans && hasSpansEnabled(clientOptions)) {
234-
serverSpans.setup(client);
235-
}
236-
},
237-
238229
setupOnce() {
239230
const clientOptions = (getClient<NodeClient>()?.getOptions() || {}) satisfies Partial<NodeClientOptions>;
240231
const useOtelHttpInstrumentation = _shouldUseOtelHttpInstrumentation(options, clientOptions);
@@ -256,6 +247,13 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) =>
256247
instrumentOtelHttp(instrumentationConfig);
257248
}
258249
},
250+
afterAllSetup(client: NodeClient) {
251+
const clientOptions = client.getOptions();
252+
253+
if (enableServerSpans && hasSpansEnabled(clientOptions)) {
254+
serverSpans.afterAllSetup(client);
255+
}
256+
},
259257
processEvent(event) {
260258
// Note: We always run this, even if spans are disabled
261259
// The reason being that e.g. the remix integration disables span creation here but still wants to use the ignore status codes option

0 commit comments

Comments
 (0)