Skip to content

Commit 8605816

Browse files
author
cod1k
committed
Refactor Durable Object instrumentation logic
Simplified the prototype instrumentation logic by replacing the manual mapping of methods with proxies. Added tests to ensure prototype methods are properly instrumented and maintain consistency.
1 parent 52a4fa1 commit 8605816

File tree

2 files changed

+35
-43
lines changed

2 files changed

+35
-43
lines changed

packages/cloudflare/src/durableobject.ts

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ function wrapMethodWithSentry<T extends OriginalMethod>(
3131
wrapperOptions: MethodWrapperOptions,
3232
handler: T,
3333
callback?: (...args: Parameters<T>) => void,
34+
noMark?: true,
3435
): T {
3536
if (isInstrumented(handler)) {
3637
return handler;
3738
}
3839

39-
markAsInstrumented(handler);
40+
if (!noMark) {
41+
markAsInstrumented(handler);
42+
}
4043

4144
return new Proxy(handler, {
4245
apply(target, thisArg, args: Parameters<T>) {
@@ -235,49 +238,31 @@ function instrumentPrototype<T extends NewableFunction>(
235238
target: T,
236239
options: CloudflareOptions,
237240
context: MethodWrapperOptions['context'],
238-
): typeof target.prototype {
239-
const sentryMethods = new Map<string | symbol, OriginalMethod>();
240-
let proto = target.prototype;
241-
const instrumentedPrototype = new Proxy(proto, {
241+
): T {
242+
return new Proxy(target.prototype, {
242243
get(target, prop, receiver) {
243-
if (sentryMethods.has(prop)) {
244-
return sentryMethods.get(prop);
244+
const value = Reflect.get(target, prop, receiver);
245+
if (prop === 'constructor' || typeof value !== 'function') {
246+
return value;
245247
}
246-
return Reflect.get(target, prop, receiver);
248+
const wrapped = wrapMethodWithSentry(
249+
{ options, context, spanName: prop.toString(), spanOp: 'rpc' },
250+
value,
251+
undefined,
252+
true,
253+
);
254+
const instrumented = new Proxy(wrapped, {
255+
get(target, p, receiver) {
256+
if ('__SENTRY_INSTRUMENTED__' === p) {
257+
return true;
258+
}
259+
return Reflect.get(target, p, receiver);
260+
},
261+
});
262+
Object.defineProperty(receiver, prop, {
263+
value: instrumented,
264+
});
265+
return instrumented;
247266
},
248267
});
249-
while (proto && proto !== Object.prototype) {
250-
for (const method of Object.getOwnPropertyNames(proto)) {
251-
if (method === 'constructor' || sentryMethods.has(method)) {
252-
continue;
253-
}
254-
255-
const value = Reflect.get(proto, method, proto);
256-
if (typeof value === 'function') {
257-
sentryMethods.set(
258-
method,
259-
wrapMethodWithSentry(
260-
{
261-
options,
262-
context,
263-
spanName: method,
264-
spanOp: 'rpc',
265-
},
266-
// <editor-fold desc="Disable __SENTRY_INSTRUMENTED__ for prototype methods">
267-
new Proxy(value, {
268-
set(target, p, newValue, receiver): boolean {
269-
if ('__SENTRY_INSTRUMENTED__' === p) {
270-
return true;
271-
}
272-
return Reflect.set(target, p, newValue, receiver);
273-
},
274-
}),
275-
// </editor-fold>
276-
),
277-
);
278-
}
279-
}
280-
proto = Object.getPrototypeOf(proto);
281-
}
282-
return instrumentedPrototype;
283268
}

packages/cloudflare/test/durableobject.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ describe('instrumentDurableObjectWithSentry', () => {
1515
expect(() => Reflect.construct(instrumented, [])).not.toThrow();
1616
expect(options).toHaveBeenCalledOnce();
1717
});
18+
it('Instruments prototype methods and defines implementation in the object', () => {
19+
const testClass = class {
20+
method() {}
21+
};
22+
const obj = Reflect.construct(instrumentDurableObjectWithSentry(vi.fn(), testClass as any), []) as any;
23+
expect(obj.method).toBe(obj.method);
24+
});
1825
it('Instruments prototype methods without "sticking" to the options', () => {
1926
const initCore = vi.spyOn(SentryCore, 'initAndBind');
2027
vi.spyOn(SentryCore, 'getClient').mockReturnValue(undefined);
@@ -53,14 +60,14 @@ describe('instrumentDurableObjectWithSentry', () => {
5360
const instrumented = instrumentDurableObjectWithSentry(vi.fn(), testClass as any);
5461
const obj = Reflect.construct(instrumented, []);
5562
expect(Object.getPrototypeOf(obj), 'Prototype is instrumented').not.toBe(testClass.prototype);
56-
expect(isInstrumented((obj as any)['rpcMethod']), 'RPC method').toBeFalsy();
5763
for (const method_name of [
5864
'propertyFunction',
5965
'fetch',
6066
'alarm',
6167
'webSocketMessage',
6268
'webSocketClose',
6369
'webSocketError',
70+
'rpcMethod',
6471
]) {
6572
expect(isInstrumented((obj as any)[method_name]), `Method ${method_name} is instrumented`).toBeTruthy();
6673
}

0 commit comments

Comments
 (0)