Skip to content

Commit cac0b5b

Browse files
committed
ref(core): Avoid using SentryError for event processing control flow
1 parent c276386 commit cac0b5b

File tree

3 files changed

+48
-59
lines changed

3 files changed

+48
-59
lines changed

packages/core/src/client.ts

+48-15
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ import {
5353
import { createClientReportEnvelope } from './utils-hoist/clientreport';
5454
import { dsnToString, makeDsn } from './utils-hoist/dsn';
5555
import { addItemToEnvelope, createAttachmentEnvelopeItem } from './utils-hoist/envelope';
56-
import { SentryError } from './utils-hoist/error';
5756
import { isParameterizedString, isPlainObject, isPrimitive, isThenable } from './utils-hoist/is';
5857
import { logger } from './utils-hoist/logger';
5958
import { checkOrSetAlreadyCaught, uuid4 } from './utils-hoist/misc';
@@ -69,6 +68,41 @@ import { _getSpanForScope } from './utils/spanOnScope';
6968
const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured.";
7069
const MISSING_RELEASE_FOR_SESSION_ERROR = 'Discarded session because of missing or non-string release';
7170

71+
const INTERNAL_ERROR_SYMBOL = Symbol.for('SentryInternalError');
72+
const DO_NOT_SEND_EVENT_SYMBOL = Symbol.for('SentryDoNotSendEventError');
73+
74+
interface InternalError {
75+
message: string;
76+
[INTERNAL_ERROR_SYMBOL]: true;
77+
}
78+
79+
interface DoNotSendEventError {
80+
message: string;
81+
[DO_NOT_SEND_EVENT_SYMBOL]: true;
82+
}
83+
84+
function makeInternalError(message: string): InternalError {
85+
return {
86+
message,
87+
[INTERNAL_ERROR_SYMBOL]: true,
88+
};
89+
}
90+
91+
function makeDoNotSendEventError(message: string): DoNotSendEventError {
92+
return {
93+
message,
94+
[DO_NOT_SEND_EVENT_SYMBOL]: true,
95+
};
96+
}
97+
98+
function isInternalError(error: unknown): error is InternalError {
99+
return !!error && typeof error === 'object' && INTERNAL_ERROR_SYMBOL in error;
100+
}
101+
102+
function isDoNotSendEventError(error: unknown): error is DoNotSendEventError {
103+
return !!error && typeof error === 'object' && DO_NOT_SEND_EVENT_SYMBOL in error;
104+
}
105+
72106
/**
73107
* Base implementation for all JavaScript SDK clients.
74108
*
@@ -975,10 +1009,10 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
9751009
},
9761010
reason => {
9771011
if (DEBUG_BUILD) {
978-
// If something's gone wrong, log the error as a warning. If it's just us having used a `SentryError` for
979-
// control flow, log just the message (no stack) as a log-level log.
980-
if (reason instanceof SentryError && reason.logLevel === 'log') {
1012+
if (isDoNotSendEventError(reason)) {
9811013
logger.log(reason.message);
1014+
} else if (isInternalError(reason)) {
1015+
logger.warn(reason.message);
9821016
} else {
9831017
logger.warn(reason);
9841018
}
@@ -1022,9 +1056,8 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10221056
if (isError && typeof parsedSampleRate === 'number' && Math.random() > parsedSampleRate) {
10231057
this.recordDroppedEvent('sample_rate', 'error');
10241058
return rejectedSyncPromise(
1025-
new SentryError(
1059+
makeDoNotSendEventError(
10261060
`Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`,
1027-
'log',
10281061
),
10291062
);
10301063
}
@@ -1035,7 +1068,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10351068
.then(prepared => {
10361069
if (prepared === null) {
10371070
this.recordDroppedEvent('event_processor', dataCategory);
1038-
throw new SentryError('An event processor returned `null`, will not send event.', 'log');
1071+
throw makeDoNotSendEventError('An event processor returned `null`, will not send event.');
10391072
}
10401073

10411074
const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true;
@@ -1055,7 +1088,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10551088
const spanCount = 1 + spans.length;
10561089
this.recordDroppedEvent('before_send', 'span', spanCount);
10571090
}
1058-
throw new SentryError(`${beforeSendLabel} returned \`null\`, will not send event.`, 'log');
1091+
throw makeDoNotSendEventError(`${beforeSendLabel} returned \`null\`, will not send event.`);
10591092
}
10601093

10611094
const session = currentScope.getSession() || isolationScope.getSession();
@@ -1089,7 +1122,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10891122
return processedEvent;
10901123
})
10911124
.then(null, reason => {
1092-
if (reason instanceof SentryError) {
1125+
if (isDoNotSendEventError(reason) || isInternalError(reason)) {
10931126
throw reason;
10941127
}
10951128

@@ -1099,7 +1132,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10991132
},
11001133
originalException: reason,
11011134
});
1102-
throw new SentryError(
1135+
throw makeInternalError(
11031136
`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${reason}`,
11041137
);
11051138
});
@@ -1204,17 +1237,17 @@ function _validateBeforeSendResult(
12041237
if (isThenable(beforeSendResult)) {
12051238
return beforeSendResult.then(
12061239
event => {
1207-
if (!isPlainObject(event) && event !== null) {
1208-
throw new SentryError(invalidValueError);
1240+
if (!isPlainObject(event) && event) {
1241+
throw makeInternalError(invalidValueError);
12091242
}
12101243
return event;
12111244
},
12121245
e => {
1213-
throw new SentryError(`${beforeSendLabel} rejected with ${e}`);
1246+
throw makeInternalError(`${beforeSendLabel} rejected with ${e}`);
12141247
},
12151248
);
1216-
} else if (!isPlainObject(beforeSendResult) && beforeSendResult !== null) {
1217-
throw new SentryError(invalidValueError);
1249+
} else if (!isPlainObject(beforeSendResult) && beforeSendResult) {
1250+
throw makeInternalError(invalidValueError);
12181251
}
12191252
return beforeSendResult;
12201253
}

packages/core/src/integrations/eventFilters.ts

-17
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,12 @@ function _mergeOptions(
102102
...(internalOptions.disableErrorDefaults ? [] : DEFAULT_IGNORE_ERRORS),
103103
],
104104
ignoreTransactions: [...(internalOptions.ignoreTransactions || []), ...(clientOptions.ignoreTransactions || [])],
105-
ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true,
106105
};
107106
}
108107

109108
function _shouldDropEvent(event: Event, options: Partial<EventFiltersOptions>): boolean {
110109
if (!event.type) {
111110
// Filter errors
112-
113-
if (options.ignoreInternal && _isSentryError(event)) {
114-
DEBUG_BUILD &&
115-
logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`);
116-
return true;
117-
}
118111
if (_isIgnoredError(event, options.ignoreErrors)) {
119112
DEBUG_BUILD &&
120113
logger.warn(
@@ -196,16 +189,6 @@ function _isAllowedUrl(event: Event, allowUrls?: Array<string | RegExp>): boolea
196189
return !url ? true : stringMatchesSomePattern(url, allowUrls);
197190
}
198191

199-
function _isSentryError(event: Event): boolean {
200-
try {
201-
// @ts-expect-error can't be a sentry error if undefined
202-
return event.exception.values[0].type === 'SentryError';
203-
} catch (e) {
204-
// ignore
205-
}
206-
return false;
207-
}
208-
209192
function _getLastValidUrl(frames: StackFrame[] = []): string | null {
210193
for (let i = frames.length - 1; i >= 0; i--) {
211194
const frame = frames[i];

packages/core/test/lib/integrations/eventFilters.test.ts

-27
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,6 @@ const EVENT_WITH_VALUE: Event = {
311311
},
312312
};
313313

314-
const SENTRY_EVENT: Event = {
315-
exception: {
316-
values: [
317-
{
318-
type: 'SentryError',
319-
value: 'something something server connection',
320-
},
321-
],
322-
},
323-
};
324-
325314
const SCRIPT_ERROR_EVENT: Event = {
326315
exception: {
327316
values: [
@@ -425,22 +414,6 @@ describe.each([
425414
['InboundFilters', inboundFiltersIntegration],
426415
['EventFilters', eventFiltersIntegration],
427416
])('%s', (_, integrationFn) => {
428-
describe('_isSentryError', () => {
429-
it('should work as expected', () => {
430-
const eventProcessor = createEventFiltersEventProcessor(integrationFn);
431-
expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT);
432-
expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT);
433-
expect(eventProcessor(SENTRY_EVENT, {})).toBe(null);
434-
});
435-
436-
it('should be configurable', () => {
437-
const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreInternal: false });
438-
expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT);
439-
expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT);
440-
expect(eventProcessor(SENTRY_EVENT, {})).toBe(SENTRY_EVENT);
441-
});
442-
});
443-
444417
describe('ignoreErrors', () => {
445418
it('string filter with partial match', () => {
446419
const eventProcessor = createEventFiltersEventProcessor(integrationFn, {

0 commit comments

Comments
 (0)