Skip to content

Commit d76f57e

Browse files
committed
replace proxy with wrap
1 parent 952373e commit d76f57e

File tree

1 file changed

+53
-57
lines changed

1 file changed

+53
-57
lines changed
Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
2-
import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
2+
import { InstrumentationBase, InstrumentationNodeModuleDefinition, isWrapped } from '@opentelemetry/instrumentation';
33
import {
44
getActiveSpan,
55
getRootSpan,
@@ -37,75 +37,71 @@ export class ReactRouterInstrumentation extends InstrumentationBase<Instrumentat
3737
COMPONENT,
3838
supportedVersions,
3939
(moduleExports: ReactRouterModuleExports) => {
40-
return this._createPatchedModuleProxy(moduleExports);
40+
if (isWrapped(moduleExports['createRequestHandler'])) {
41+
this._unwrap(moduleExports, 'createRequestHandler');
42+
}
43+
this._wrap(moduleExports, 'createRequestHandler', this._patchCreateRequestHandler());
44+
return moduleExports;
4145
},
42-
(_moduleExports: unknown) => {
43-
// nothing to unwrap here
44-
return _moduleExports;
46+
(moduleExports: ReactRouterModuleExports) => {
47+
this._unwrap(moduleExports, 'createRequestHandler');
4548
},
4649
);
4750

4851
return reactRouterServerModule;
4952
}
5053

5154
/**
52-
* Creates a proxy around the React Router module exports that patches the createRequestHandler function.
53-
* This allows us to wrap the request handler to add performance monitoring for data loaders and actions.
55+
* Returns a patched version of the createRequestHandler function that adds Sentry performance monitoring.
56+
* This wraps the request handler to create spans for data loader and action requests.
5457
*/
55-
private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {
56-
return new Proxy(moduleExports, {
57-
get(target, prop, receiver) {
58-
if (prop === 'createRequestHandler') {
59-
const original = target[prop];
60-
return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {
61-
const originalRequestHandler = original.apply(this, args);
62-
63-
return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {
64-
let url: URL;
65-
try {
66-
url = new URL(request.url);
67-
} catch (error) {
68-
return originalRequestHandler(request, initialContext);
69-
}
58+
private _patchCreateRequestHandler(): (original: typeof reactRouter.createRequestHandler) => any {
59+
return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {
60+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
61+
// @ts-ignore not sure why original isn't found here?
62+
const originalRequestHandler = (original as typeof reactRouter.createRequestHandler).apply(this, args);
63+
return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {
64+
let url: URL;
65+
try {
66+
url = new URL(request.url);
67+
} catch (error) {
68+
return originalRequestHandler(request, initialContext);
69+
}
7070

71-
// We currently just want to trace loaders and actions
72-
if (!isDataRequest(url.pathname)) {
73-
return originalRequestHandler(request, initialContext);
74-
}
71+
// We currently just want to trace loaders and actions
72+
if (!isDataRequest(url.pathname)) {
73+
return originalRequestHandler(request, initialContext);
74+
}
7575

76-
const activeSpan = getActiveSpan();
77-
const rootSpan = activeSpan && getRootSpan(activeSpan);
76+
const activeSpan = getActiveSpan();
77+
const rootSpan = activeSpan && getRootSpan(activeSpan);
7878

79-
if (!rootSpan) {
80-
DEBUG_BUILD && logger.debug('No active root span found, skipping tracing for data request');
81-
return originalRequestHandler(request, initialContext);
82-
}
79+
if (!rootSpan) {
80+
DEBUG_BUILD && logger.debug('No active root span found, skipping tracing for data request');
81+
return originalRequestHandler(request, initialContext);
82+
}
8383

84-
// Set the source and overwrite attributes on the root span to ensure the transaction name
85-
// is derived from the raw URL pathname rather than any parameterized route that may be set later
86-
// TODO: try to set derived parameterized route from build here (args[0])
87-
rootSpan.setAttributes({
88-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
89-
[SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE]: `${request.method} ${url.pathname}`,
90-
});
84+
// Set the source and overwrite attributes on the root span to ensure the transaction name
85+
// is derived from the raw URL pathname rather than any parameterized route that may be set later
86+
// TODO: try to set derived parameterized route from build here (args[0])
87+
rootSpan.setAttributes({
88+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
89+
[SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE]: `${request.method} ${url.pathname}`,
90+
});
9191

92-
return startSpan(
93-
{
94-
name: getSpanName(url.pathname, request.method),
95-
attributes: {
96-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router',
97-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),
98-
},
99-
},
100-
() => {
101-
return originalRequestHandler(request, initialContext);
102-
},
103-
);
104-
};
105-
};
106-
}
107-
return Reflect.get(target, prop, receiver);
108-
},
109-
});
92+
return startSpan(
93+
{
94+
name: getSpanName(url.pathname, request.method),
95+
attributes: {
96+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router',
97+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),
98+
},
99+
},
100+
() => {
101+
return originalRequestHandler(request, initialContext);
102+
},
103+
);
104+
};
105+
};
110106
}
111107
}

0 commit comments

Comments
 (0)