Skip to content

Commit be83d3f

Browse files
committed
ref(core): Avoid using SentryError for event processing control flow
1 parent 2e26182 commit be83d3f

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
*
@@ -963,10 +997,10 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
963997
},
964998
reason => {
965999
if (DEBUG_BUILD) {
966-
// If something's gone wrong, log the error as a warning. If it's just us having used a `SentryError` for
967-
// control flow, log just the message (no stack) as a log-level log.
968-
if (reason instanceof SentryError && reason.logLevel === 'log') {
1000+
if (isDoNotSendEventError(reason)) {
9691001
logger.log(reason.message);
1002+
} else if (isInternalError(reason)) {
1003+
logger.warn(reason.message);
9701004
} else {
9711005
logger.warn(reason);
9721006
}
@@ -1010,9 +1044,8 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10101044
if (isError && typeof parsedSampleRate === 'number' && Math.random() > parsedSampleRate) {
10111045
this.recordDroppedEvent('sample_rate', 'error');
10121046
return rejectedSyncPromise(
1013-
new SentryError(
1047+
makeDoNotSendEventError(
10141048
`Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`,
1015-
'log',
10161049
),
10171050
);
10181051
}
@@ -1023,7 +1056,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10231056
.then(prepared => {
10241057
if (prepared === null) {
10251058
this.recordDroppedEvent('event_processor', dataCategory);
1026-
throw new SentryError('An event processor returned `null`, will not send event.', 'log');
1059+
throw makeDoNotSendEventError('An event processor returned `null`, will not send event.');
10271060
}
10281061

10291062
const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true;
@@ -1043,7 +1076,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10431076
const spanCount = 1 + spans.length;
10441077
this.recordDroppedEvent('before_send', 'span', spanCount);
10451078
}
1046-
throw new SentryError(`${beforeSendLabel} returned \`null\`, will not send event.`, 'log');
1079+
throw makeDoNotSendEventError(`${beforeSendLabel} returned \`null\`, will not send event.`);
10471080
}
10481081

10491082
const session = currentScope.getSession() || isolationScope.getSession();
@@ -1077,7 +1110,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10771110
return processedEvent;
10781111
})
10791112
.then(null, reason => {
1080-
if (reason instanceof SentryError) {
1113+
if (isDoNotSendEventError(reason) || isInternalError(reason)) {
10811114
throw reason;
10821115
}
10831116

@@ -1087,7 +1120,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10871120
},
10881121
originalException: reason,
10891122
});
1090-
throw new SentryError(
1123+
throw makeInternalError(
10911124
`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${reason}`,
10921125
);
10931126
});
@@ -1192,17 +1225,17 @@ function _validateBeforeSendResult(
11921225
if (isThenable(beforeSendResult)) {
11931226
return beforeSendResult.then(
11941227
event => {
1195-
if (!isPlainObject(event) && event !== null) {
1196-
throw new SentryError(invalidValueError);
1228+
if (!isPlainObject(event) && event) {
1229+
throw makeInternalError(invalidValueError);
11971230
}
11981231
return event;
11991232
},
12001233
e => {
1201-
throw new SentryError(`${beforeSendLabel} rejected with ${e}`);
1234+
throw makeInternalError(`${beforeSendLabel} rejected with ${e}`);
12021235
},
12031236
);
1204-
} else if (!isPlainObject(beforeSendResult) && beforeSendResult !== null) {
1205-
throw new SentryError(invalidValueError);
1237+
} else if (!isPlainObject(beforeSendResult) && beforeSendResult) {
1238+
throw makeInternalError(invalidValueError);
12061239
}
12071240
return beforeSendResult;
12081241
}

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)