Skip to content

Commit 8160eee

Browse files
authored
chore(sentry): add some debug logs (#500)
* add logs to sentry worker * tune logs * Update index.ts * fix tests
1 parent 7098e69 commit 8160eee

File tree

3 files changed

+84
-14
lines changed

3 files changed

+84
-14
lines changed

jest.setup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const mockedAmqpChannel = {
1010
close: jest.fn(),
1111
assertQueue: jest.fn(),
1212
prefetch: jest.fn(),
13-
sendToQueue: jest.fn(),
13+
sendToQueue: jest.fn().mockReturnValue(true),
1414
on: jest.fn(),
1515
consume: jest.fn().mockReturnValue('mockedTag'),
1616
};

lib/worker.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,9 @@ export abstract class Worker {
330330
console.log('handle error');
331331
console.log(e);
332332

333-
HawkCatcher.send(e, context);
333+
HawkCatcher.send(e, Object.assign(context, {
334+
worker: this.type,
335+
}));
334336

335337
switch (e.constructor) {
336338
case CriticalError:

workers/sentry/src/index.ts

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,24 @@ export default class SentryEventWorker extends Worker {
3939

4040
const [headers, items] = envelope;
4141

42+
if (items.length === 0) {
43+
this.logger.warn('Received envelope with no items');
44+
return;
45+
}
46+
47+
let processedCount = 0;
48+
let skippedCount = 0;
49+
4250
for (const item of items) {
43-
await this.handleEnvelopeItem(headers, item, event.projectId);
51+
const result = await this.handleEnvelopeItem(headers, item, event.projectId);
52+
if (result === 'processed') {
53+
processedCount++;
54+
} else if (result === 'skipped') {
55+
skippedCount++;
56+
}
4457
}
58+
59+
this.logger.verbose(`Processed ${processedCount} events, skipped ${skippedCount} non-event items from envelope`);
4560
} catch (error) {
4661
this.logger.error(`Error handling Sentry event task:`, error);
4762
this.logger.info('👇 Here is the problematic event:');
@@ -99,8 +114,9 @@ export default class SentryEventWorker extends Worker {
99114
* @param envelopeHeaders - The whole envelope headers
100115
* @param item - Sentry item
101116
* @param projectId - Sentry project ID
117+
* @returns 'processed' if event was sent, 'skipped' if non-event item, throws error on failure
102118
*/
103-
private async handleEnvelopeItem(envelopeHeaders: Envelope[0], item: EnvelopeItem, projectId: string): Promise<void> {
119+
private async handleEnvelopeItem(envelopeHeaders: Envelope[0], item: EnvelopeItem, projectId: string): Promise<'processed' | 'skipped'> {
104120
try {
105121
const [itemHeader, itemPayload] = item;
106122

@@ -112,7 +128,8 @@ export default class SentryEventWorker extends Worker {
112128
* Skip non-event items
113129
*/
114130
if (itemHeader.type !== 'event') {
115-
return;
131+
this.logger.info(`Skipping non-event item of type: ${itemHeader.type}`);
132+
return 'skipped';
116133
}
117134
const payloadHasSDK = typeof itemPayload === 'object' && 'sdk' in itemPayload;
118135

@@ -121,18 +138,53 @@ export default class SentryEventWorker extends Worker {
121138
*/
122139
const sentryJsSDK = ['browser', 'react', 'vue', 'angular', 'capacirtor', 'electron'];
123140

124-
const isJsSDK = payloadHasSDK && sentryJsSDK.includes(itemPayload.sdk.name);
141+
/**
142+
* Safely check if SDK name exists and is in the list
143+
* SDK name can be either a simple name like "react" or a full name like "sentry.javascript.react"
144+
*/
145+
const sdkName = payloadHasSDK && itemPayload.sdk && typeof itemPayload.sdk === 'object' && 'name' in itemPayload.sdk
146+
? itemPayload.sdk.name
147+
: undefined;
148+
149+
/**
150+
* Check if SDK is a JavaScript-related SDK
151+
* Supports both simple names (e.g., "react") and full names (e.g., "sentry.javascript.react")
152+
*/
153+
const isJsSDK = sdkName !== undefined && typeof sdkName === 'string' && (
154+
/**
155+
* Exact match for simple SDK names (e.g., "react", "browser")
156+
*/
157+
sentryJsSDK.includes(sdkName) ||
158+
/**
159+
* Check if SDK name contains one of the JS SDK names
160+
* Examples:
161+
* - "sentry.javascript.react" matches "react"
162+
* - "sentry.javascript.browser" matches "browser"
163+
* - "@sentry/react" matches "react"
164+
*/
165+
sentryJsSDK.some((jsSDK) => sdkName.includes(jsSDK))
166+
);
125167

126168
const hawkEvent = this.transformToHawkFormat(envelopeHeaders as EventEnvelope[0], item as EventItem, projectId, isJsSDK);
127169

128170
/**
129-
* If we have release attached to the event
171+
* Send task to appropriate worker and check if it was successfully queued
130172
*/
131-
if (isJsSDK) {
132-
await this.addTask(WorkerNames.JAVASCRIPT, hawkEvent as JavaScriptEventWorkerTask);
133-
} else {
134-
await this.addTask(WorkerNames.DEFAULT, hawkEvent as DefaultEventWorkerTask);
173+
const workerName = isJsSDK ? WorkerNames.JAVASCRIPT : WorkerNames.DEFAULT;
174+
const taskSent = await this.addTask(workerName, hawkEvent as JavaScriptEventWorkerTask | DefaultEventWorkerTask);
175+
176+
if (!taskSent) {
177+
/**
178+
* If addTask returns false, the message was not queued (queue full or channel closed)
179+
*/
180+
const error = new Error(`Failed to queue event to ${workerName} worker. Queue may be full or channel closed.`);
181+
this.logger.error(error.message);
182+
this.logger.info('👇 Here is the event that failed to queue:');
183+
this.logger.json(hawkEvent);
184+
throw error;
135185
}
186+
187+
return 'processed';
136188
} catch (error) {
137189
this.logger.error('Error handling envelope item:', error);
138190
this.logger.info('👇 Here is the problematic item:');
@@ -162,7 +214,14 @@ export default class SentryEventWorker extends Worker {
162214
* convert sent_at from ISO 8601 to Unix timestamp
163215
*/
164216
const msInSecond = 1000;
165-
const sentAtUnix = Math.floor(new Date(sent_at).getTime() / msInSecond);
217+
const sentAtDate = new Date(sent_at);
218+
const sentAtTime = sentAtDate.getTime();
219+
220+
if (isNaN(sentAtTime)) {
221+
throw new Error(`Invalid sent_at timestamp: ${sent_at}`);
222+
}
223+
224+
const sentAtUnix = Math.floor(sentAtTime / msInSecond);
166225
/* eslint-enable @typescript-eslint/naming-convention */
167226

168227
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
@@ -175,9 +234,18 @@ export default class SentryEventWorker extends Worker {
175234
* We need to decode it to JSON
176235
*/
177236
if (eventPayload instanceof Uint8Array) {
178-
const textDecoder = new TextDecoder();
237+
try {
238+
const textDecoder = new TextDecoder();
239+
const decoded = textDecoder.decode(eventPayload as Uint8Array);
179240

180-
eventPayload = JSON.parse(textDecoder.decode(eventPayload as Uint8Array));
241+
try {
242+
eventPayload = JSON.parse(decoded);
243+
} catch (parseError) {
244+
throw new Error(`Failed to parse event payload JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
245+
}
246+
} catch (decodeError) {
247+
throw new Error(`Failed to decode Uint8Array event payload: ${decodeError instanceof Error ? decodeError.message : String(decodeError)}`);
248+
}
181249
}
182250

183251
const title = composeTitle(eventPayload);

0 commit comments

Comments
 (0)