Skip to content

Commit 6c90d23

Browse files
committed
merge event names
1 parent 7ba1fbd commit 6c90d23

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/listeners/test-event.listener.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class TestEventListener {
1818
@OnEvent('multiple.first')
1919
@OnEvent('multiple.second')
2020
async handleMultipleEvents(payload: any): Promise<void> {
21-
Sentry.setTag('payload', payload);
21+
Sentry.setTag(payload.data, true);
2222
await new Promise(resolve => setTimeout(resolve, 100));
2323
}
2424
}

dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/events.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,24 @@ test('Event emitter', async () => {
4343

4444
test('Multiple OnEvent decorators', async () => {
4545
const firstTxPromise = waitForTransaction('nestjs-distributed-tracing', transactionEvent => {
46-
return transactionEvent.transaction === 'event multiple.first';
46+
return transactionEvent.transaction === 'event multiple.first|multiple.second';
4747
});
4848
const secondTxPromise = waitForTransaction('nestjs-distributed-tracing', transactionEvent => {
49-
return transactionEvent.transaction === 'event multiple.second';
49+
return transactionEvent.transaction === 'event multiple.first|multiple.second';
50+
});
51+
const rootPromise = waitForTransaction('nestjs-distributed-tracing', transactionEvent => {
52+
return transactionEvent.transaction === 'GET /events/emit-multiple';
5053
});
5154

5255
const eventsUrl = `http://localhost:3050/events/emit-multiple`;
5356
await fetch(eventsUrl);
5457

5558
const firstTx = await firstTxPromise;
5659
const secondTx = await secondTxPromise;
60+
const rootTx = await rootPromise;
5761

58-
expect(firstTx.tags).toMatchObject({ payload: { data: 'test-first' } });
59-
expect(secondTx.tags).toMatchObject({ payload: { data: 'test-second' } });
62+
expect(firstTx).toBeDefined();
63+
expect(secondTx).toBeDefined();
64+
// assert that the correct payloads were added
65+
expect(rootTx.tags).toMatchObject({ 'test-first': true, 'test-second': true });
6066
});

packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,31 +58,46 @@ export class SentryNestEventInstrumentation extends InstrumentationBase {
5858
private _createWrapOnEvent() {
5959
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6060
return function wrapOnEvent(original: any) {
61-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
62-
return function wrappedOnEvent(event: any, options?: any) {
63-
const eventName = Array.isArray(event)
64-
? event.join(',')
65-
: typeof event === 'string' || typeof event === 'symbol'
66-
? event.toString()
67-
: '<unknown_event>';
68-
61+
return function wrappedOnEvent(event: unknown, options?: unknown) {
6962
// Get the original decorator result
7063
const decoratorResult = original(event, options);
7164

7265
// Return a new decorator function that wraps the handler
73-
return function (target: OnEventTarget, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
74-
if (!descriptor.value || typeof descriptor.value !== 'function' || target.__SENTRY_INTERNAL__) {
66+
return (target: OnEventTarget, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
67+
if (
68+
!descriptor.value ||
69+
typeof descriptor.value !== 'function' ||
70+
target.__SENTRY_INTERNAL__ ||
71+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
72+
descriptor.value.__SENTRY_INSTRUMENTED__
73+
) {
7574
return decoratorResult(target, propertyKey, descriptor);
7675
}
7776

78-
// Get the original handler
7977
const originalHandler = descriptor.value;
8078
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
8179
const handlerName = originalHandler.name || propertyKey;
80+
let eventName = typeof event === 'string' ? event : String(event);
81+
82+
// Instrument the actual handler
83+
descriptor.value = async function (...args: unknown[]) {
84+
// When multiple @OnEvent decorators are used on a single method, we need to get all event names
85+
// from the reflector metadata as there is no information during execution which event triggered it
86+
if (Reflect.getMetadataKeys(descriptor.value).includes('EVENT_LISTENER_METADATA')) {
87+
const eventData = Reflect.getMetadata('EVENT_LISTENER_METADATA', descriptor.value);
88+
if (Array.isArray(eventData) && eventData.length > 0) {
89+
eventName = eventData
90+
.map((data: unknown) => {
91+
if (data && typeof data === 'object' && 'event' in data && data.event) {
92+
return data.event;
93+
}
94+
return '';
95+
})
96+
.reverse() // decorators are evaluated bottom to top
97+
.join('|');
98+
}
99+
}
82100

83-
// Instrument the handler
84-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
85-
descriptor.value = async function (...args: any[]) {
86101
return startSpan(getEventSpanOptions(eventName), async () => {
87102
try {
88103
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -96,6 +111,9 @@ export class SentryNestEventInstrumentation extends InstrumentationBase {
96111
});
97112
};
98113

114+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
115+
descriptor.value.__SENTRY_INSTRUMENTED__ = true;
116+
99117
// Preserve the original function name
100118
Object.defineProperty(descriptor.value, 'name', {
101119
value: handlerName,

0 commit comments

Comments
 (0)