-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
ref(core): Reduce inboundfilters bundle size #4625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
5515dd8
a4e6425
48134b2
174ffe2
c1e171d
7f01599
ecb6460
beffc9f
f2bfe03
c277576
94eac23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,12 @@ | ||
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; | ||
import { Event, Integration, StackFrame } from '@sentry/types'; | ||
import { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types'; | ||
import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils'; | ||
|
||
// "Script error." is hard coded into browsers for errors that it can't read. | ||
// this is the result of a script being pulled in from an external domain and CORS. | ||
const DEFAULT_IGNORE_ERRORS = [/^Script error\.?$/, /^Javascript error: Script error\.? on line 0$/]; | ||
|
||
/** JSDoc */ | ||
interface InboundFiltersOptions { | ||
export interface InboundFiltersOptions { | ||
allowUrls: Array<string | RegExp>; | ||
denyUrls: Array<string | RegExp>; | ||
ignoreErrors: Array<string | RegExp>; | ||
|
@@ -36,190 +35,180 @@ export class InboundFilters implements Integration { | |
/** | ||
* @inheritDoc | ||
*/ | ||
public setupOnce(): void { | ||
public setupOnce(addGlobalEventProcessor: (processor: EventProcessor) => void, getCurrentHub: () => Hub): void { | ||
addGlobalEventProcessor((event: Event) => { | ||
const hub = getCurrentHub(); | ||
if (!hub) { | ||
return event; | ||
} | ||
const self = hub.getIntegration(InboundFilters); | ||
if (self) { | ||
const client = hub.getClient(); | ||
const clientOptions = client ? client.getOptions() : {}; | ||
// This checks prevents most of the occurrences of the bug linked below: | ||
// https://github.com/getsentry/sentry-javascript/issues/2622 | ||
// The bug is caused by multiple SDK instances, where one is minified and one is using non-mangled code. | ||
// Unfortunatelly we cannot fix it reliably (thus reserved property in rollup's terser config), | ||
// as we cannot force people using multiple instances in their apps to sync SDK versions. | ||
const options = typeof self._mergeOptions === 'function' ? self._mergeOptions(clientOptions) : {}; | ||
if (typeof self._shouldDropEvent !== 'function') { | ||
return event; | ||
if (hub) { | ||
const self = hub.getIntegration(InboundFilters); | ||
if (self) { | ||
const client = hub.getClient(); | ||
const clientOptions = client ? client.getOptions() : {}; | ||
const options = _mergeOptions(self._options, clientOptions); | ||
return _shouldDropEvent(event, options) ? null : event; | ||
} | ||
return self._shouldDropEvent(event, options) ? null : event; | ||
} | ||
return event; | ||
}); | ||
} | ||
} | ||
|
||
/** JSDoc */ | ||
private _shouldDropEvent(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
if (this._isSentryError(event, options)) { | ||
isDebugBuild() && | ||
logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); | ||
return true; | ||
} | ||
if (this._isIgnoredError(event, options)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${getEventDescription(event)}`, | ||
); | ||
return true; | ||
} | ||
if (this._isDeniedUrl(event, options)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription( | ||
event, | ||
)}.\nUrl: ${this._getEventFilterUrl(event)}`, | ||
); | ||
return true; | ||
} | ||
if (!this._isAllowedUrl(event, options)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription( | ||
event, | ||
)}.\nUrl: ${this._getEventFilterUrl(event)}`, | ||
); | ||
return true; | ||
} | ||
return false; | ||
/** JSDoc */ | ||
export function _mergeOptions( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (optional) Wondering if we should drop the underscore in the extracted function names... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll leave it here for now, but I'll remove the JSDoc as per your comments below. I left it with the underscore initially to minimize the diff, and make it clear as possible how the functions moved. |
||
internalOptions: Partial<InboundFiltersOptions> = {}, | ||
clientOptions: Partial<InboundFiltersOptions> = {}, | ||
): Partial<InboundFiltersOptions> { | ||
return { | ||
allowUrls: [ | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(internalOptions.whitelistUrls || []), | ||
...(internalOptions.allowUrls || []), | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(clientOptions.whitelistUrls || []), | ||
...(clientOptions.allowUrls || []), | ||
], | ||
denyUrls: [ | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(internalOptions.blacklistUrls || []), | ||
...(internalOptions.denyUrls || []), | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(clientOptions.blacklistUrls || []), | ||
...(clientOptions.denyUrls || []), | ||
], | ||
ignoreErrors: [ | ||
...(internalOptions.ignoreErrors || []), | ||
...(clientOptions.ignoreErrors || []), | ||
...DEFAULT_IGNORE_ERRORS, | ||
], | ||
ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true, | ||
}; | ||
} | ||
|
||
/** JSDoc */ | ||
export function _shouldDropEvent(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
if (_isSentryError(event, options.ignoreInternal)) { | ||
isDebugBuild() && | ||
logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); | ||
return true; | ||
} | ||
if (_isIgnoredError(event, options.ignoreErrors)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${getEventDescription(event)}`, | ||
); | ||
return true; | ||
} | ||
if (_isDeniedUrl(event, options.denyUrls)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription( | ||
event, | ||
)}.\nUrl: ${_getEventFilterUrl(event)}`, | ||
); | ||
return true; | ||
} | ||
if (!_isAllowedUrl(event, options.allowUrls)) { | ||
isDebugBuild() && | ||
logger.warn( | ||
`Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription( | ||
event, | ||
)}.\nUrl: ${_getEventFilterUrl(event)}`, | ||
); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/** JSDoc */ | ||
private _isSentryError(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
if (!options.ignoreInternal) { | ||
return false; | ||
} | ||
/** JSDoc */ | ||
AbhiPrasad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
function _isIgnoredError(event: Event, ignoreErrors?: Array<string | RegExp>): boolean { | ||
if (!ignoreErrors || !ignoreErrors.length) { | ||
return false; | ||
} | ||
|
||
try { | ||
// @ts-ignore can't be a sentry error if undefined | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
return event.exception.values[0].type === 'SentryError'; | ||
} catch (e) { | ||
// ignore | ||
} | ||
return _getPossibleEventMessages(event).some(message => | ||
ignoreErrors.some(pattern => isMatchingPattern(message, pattern)), | ||
); | ||
} | ||
|
||
/** JSDoc */ | ||
function _isDeniedUrl(event: Event, denyUrls?: Array<string | RegExp>): boolean { | ||
// TODO: Use Glob instead? | ||
if (!denyUrls || !denyUrls.length) { | ||
return false; | ||
} | ||
const url = _getEventFilterUrl(event); | ||
return !url ? false : denyUrls.some(pattern => isMatchingPattern(url, pattern)); | ||
} | ||
|
||
/** JSDoc */ | ||
private _isIgnoredError(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
if (!options.ignoreErrors || !options.ignoreErrors.length) { | ||
return false; | ||
} | ||
|
||
return this._getPossibleEventMessages(event).some(message => | ||
// Not sure why TypeScript complains here... | ||
(options.ignoreErrors as Array<RegExp | string>).some(pattern => isMatchingPattern(message, pattern)), | ||
); | ||
/** JSDoc */ | ||
function _isAllowedUrl(event: Event, allowUrls?: Array<string | RegExp>): boolean { | ||
// TODO: Use Glob instead? | ||
if (!allowUrls || !allowUrls.length) { | ||
return true; | ||
} | ||
const url = _getEventFilterUrl(event); | ||
return !url ? true : allowUrls.some(pattern => isMatchingPattern(url, pattern)); | ||
} | ||
|
||
/** JSDoc */ | ||
private _isDeniedUrl(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
// TODO: Use Glob instead? | ||
if (!options.denyUrls || !options.denyUrls.length) { | ||
return false; | ||
/** JSDoc */ | ||
function _getPossibleEventMessages(event: Event): string[] { | ||
if (event.message) { | ||
return [event.message]; | ||
} | ||
if (event.exception) { | ||
try { | ||
const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; | ||
return [`${value}`, `${type}: ${value}`]; | ||
} catch (oO) { | ||
isDebugBuild() && logger.error(`Cannot extract message for event ${getEventDescription(event)}`); | ||
return []; | ||
} | ||
const url = this._getEventFilterUrl(event); | ||
return !url ? false : options.denyUrls.some(pattern => isMatchingPattern(url, pattern)); | ||
} | ||
return []; | ||
} | ||
|
||
/** JSDoc */ | ||
private _isAllowedUrl(event: Event, options: Partial<InboundFiltersOptions>): boolean { | ||
// TODO: Use Glob instead? | ||
if (!options.allowUrls || !options.allowUrls.length) { | ||
return true; | ||
/** JSDoc */ | ||
function _isSentryError(event: Event, ignoreInternal?: boolean): boolean { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get rid of the The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good change, will do! |
||
if (ignoreInternal) { | ||
try { | ||
// @ts-ignore can't be a sentry error if undefined | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
return event.exception.values[0].type === 'SentryError'; | ||
} catch (e) { | ||
// ignore | ||
} | ||
const url = this._getEventFilterUrl(event); | ||
return !url ? true : options.allowUrls.some(pattern => isMatchingPattern(url, pattern)); | ||
} | ||
return false; | ||
} | ||
|
||
/** JSDoc */ | ||
private _mergeOptions(clientOptions: Partial<InboundFiltersOptions> = {}): Partial<InboundFiltersOptions> { | ||
return { | ||
allowUrls: [ | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(this._options.whitelistUrls || []), | ||
...(this._options.allowUrls || []), | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(clientOptions.whitelistUrls || []), | ||
...(clientOptions.allowUrls || []), | ||
], | ||
denyUrls: [ | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(this._options.blacklistUrls || []), | ||
...(this._options.denyUrls || []), | ||
// eslint-disable-next-line deprecation/deprecation | ||
...(clientOptions.blacklistUrls || []), | ||
...(clientOptions.denyUrls || []), | ||
], | ||
ignoreErrors: [ | ||
...(this._options.ignoreErrors || []), | ||
...(clientOptions.ignoreErrors || []), | ||
...DEFAULT_IGNORE_ERRORS, | ||
], | ||
ignoreInternal: typeof this._options.ignoreInternal !== 'undefined' ? this._options.ignoreInternal : true, | ||
}; | ||
} | ||
/** JSDoc */ | ||
function _getLastValidUrl(frames: StackFrame[] = []): string | null { | ||
for (let i = frames.length - 1; i >= 0; i--) { | ||
const frame = frames[i]; | ||
|
||
/** JSDoc */ | ||
private _getPossibleEventMessages(event: Event): string[] { | ||
if (event.message) { | ||
return [event.message]; | ||
if (frame && frame.filename !== '<anonymous>' && frame.filename !== '[native code]') { | ||
return frame.filename || null; | ||
} | ||
if (event.exception) { | ||
try { | ||
const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; | ||
return [`${value}`, `${type}: ${value}`]; | ||
} catch (oO) { | ||
isDebugBuild() && logger.error(`Cannot extract message for event ${getEventDescription(event)}`); | ||
return []; | ||
} | ||
} | ||
return []; | ||
} | ||
|
||
/** JSDoc */ | ||
private _getLastValidUrl(frames: StackFrame[] = []): string | null { | ||
for (let i = frames.length - 1; i >= 0; i--) { | ||
const frame = frames[i]; | ||
return null; | ||
} | ||
|
||
if (frame && frame.filename !== '<anonymous>' && frame.filename !== '[native code]') { | ||
return frame.filename || null; | ||
} | ||
/** JSDoc */ | ||
function _getEventFilterUrl(event: Event): string | null { | ||
try { | ||
if (event.stacktrace) { | ||
return _getLastValidUrl(event.stacktrace.frames); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** JSDoc */ | ||
private _getEventFilterUrl(event: Event): string | null { | ||
let frames; | ||
try { | ||
if (event.stacktrace) { | ||
return this._getLastValidUrl(event.stacktrace.frames); | ||
} | ||
let frames; | ||
try { | ||
// @ts-ignore we only care about frames if the whole thing here is defined | ||
frames = event.exception.values[0].stacktrace.frames; | ||
} catch (e) { | ||
// ignore | ||
} | ||
return frames ? this._getLastValidUrl(frames) : null; | ||
} catch (oO) { | ||
isDebugBuild() && logger.error(`Cannot extract url for event ${getEventDescription(event)}`); | ||
return null; | ||
// @ts-ignore we only care about frames if the whole thing here is defined | ||
frames = event.exception.values[0].stacktrace.frames; | ||
} catch (e) { | ||
// ignore | ||
} | ||
return frames ? _getLastValidUrl(frames) : null; | ||
} catch (oO) { | ||
isDebugBuild() && logger.error(`Cannot extract url for event ${getEventDescription(event)}`); | ||
return null; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.