Skip to content

Commit ac38a3c

Browse files
committed
ref: Move common integrations to the core package
1 parent 801b6d0 commit ac38a3c

File tree

17 files changed

+371
-188
lines changed

17 files changed

+371
-188
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- [browser] feat: Better dedupe integration event description
6+
- [core] ref: Move Dedupe, FunctionString, InboundFilters and SdkInformation integrations to the core package
67

78
## 4.0.2
89

packages/browser/src/backend.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@ import { BeaconTransport, FetchTransport, XHRTransport } from './transports';
1111
* @see BrowserClient for more information.
1212
*/
1313
export interface BrowserOptions extends Options {
14-
/**
15-
* A pattern for error messages which should not be sent to Sentry. By
16-
* default, all errors will be sent.
17-
*/
18-
ignoreErrors?: Array<string | RegExp>;
19-
2014
/**
2115
* A pattern for error URLs which should not be sent to Sentry.
2216
* To whitelist certain errors instead, use {@link Options.whitelistUrls}.

packages/browser/src/integrations/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
export { GlobalHandlers } from './globalhandlers';
2-
export { Dedupe } from './dedupe';
3-
export { FunctionToString } from './functiontostring';
42
export { TryCatch } from './trycatch';
53
export { Breadcrumbs } from './breadcrumbs';
64
export { LinkedErrors } from './linkederrors';
7-
export { SDKInformation } from './sdkinformation';
8-
export { InboundFilters } from './inboundfilters';
95
export { ReportingObserver } from './reportingobserver';
106

117
export { Ember } from './pluggable/ember';

packages/browser/src/sdk.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
1-
import { getCurrentHub, initAndBind } from '@sentry/core';
1+
import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core';
22
import { DsnLike } from '@sentry/types';
33
import { BrowserOptions } from './backend';
44
import { BrowserClient } from './client';
5-
import {
6-
Breadcrumbs,
7-
Dedupe,
8-
FunctionToString,
9-
GlobalHandlers,
10-
InboundFilters,
11-
LinkedErrors,
12-
ReportingObserver,
13-
SDKInformation,
14-
TryCatch,
15-
} from './integrations';
5+
import { Breadcrumbs, GlobalHandlers, LinkedErrors, ReportingObserver, TryCatch } from './integrations';
6+
import { SDK_NAME, SDK_VERSION } from './version';
167

178
export const defaultIntegrations = [
18-
new Dedupe(),
19-
new FunctionToString(),
9+
// Common
10+
new CoreIntegrations.Dedupe(),
11+
new CoreIntegrations.InboundFilters(),
12+
new CoreIntegrations.FunctionToString(),
13+
new CoreIntegrations.SDKInformation({
14+
name: 'npm:@sentry/browser',
15+
sdkName: SDK_NAME,
16+
sdkVersion: SDK_VERSION,
17+
}),
18+
// Native Wrappers
2019
new TryCatch(),
2120
new Breadcrumbs(),
21+
// Global Handlers
2222
new GlobalHandlers(),
23-
new LinkedErrors(),
2423
new ReportingObserver(),
25-
new SDKInformation(),
26-
new InboundFilters(),
24+
// Misc
25+
new LinkedErrors(),
2726
];
2827

2928
/**
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import { configureScope } from '@sentry/minimal';
2+
import { Integration, SentryEvent, SentryException, StackFrame } from '@sentry/types';
3+
import { getEventDescription } from '@sentry/utils/misc';
4+
import { logger } from '../logger';
5+
6+
/** Deduplication filter */
7+
export class Dedupe implements Integration {
8+
/**
9+
* @inheritDoc
10+
*/
11+
private previousEvent?: SentryEvent;
12+
13+
/**
14+
* @inheritDoc
15+
*/
16+
public name: string = 'Dedupe';
17+
18+
/**
19+
* @inheritDoc
20+
*/
21+
public install(): void {
22+
configureScope(scope => {
23+
scope.addEventProcessor(async (currentEvent: SentryEvent) => {
24+
// Juuust in case something goes wrong
25+
try {
26+
if (this.shouldDropEvent(currentEvent, this.previousEvent)) {
27+
return null;
28+
}
29+
} catch (_oO) {
30+
return (this.previousEvent = currentEvent);
31+
}
32+
33+
return (this.previousEvent = currentEvent);
34+
});
35+
});
36+
}
37+
38+
/** JSDoc */
39+
public shouldDropEvent(currentEvent: SentryEvent, previousEvent?: SentryEvent): boolean {
40+
if (!previousEvent) {
41+
return false;
42+
}
43+
44+
if (this.isSameMessageEvent(currentEvent, previousEvent)) {
45+
logger.warn(
46+
`Event dropped due to being a duplicate of previous event (same message).\nEvent: ${getEventDescription(
47+
currentEvent,
48+
)}`,
49+
);
50+
return true;
51+
}
52+
53+
if (this.isSameExceptionEvent(currentEvent, previousEvent)) {
54+
logger.warn(
55+
`Event dropped due to being a duplicate of previous event (same exception).\nEvent: ${getEventDescription(
56+
currentEvent,
57+
)}`,
58+
);
59+
return true;
60+
}
61+
62+
return false;
63+
}
64+
65+
/** JSDoc */
66+
private isSameMessageEvent(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean {
67+
const currentMessage = currentEvent.message;
68+
const previousMessage = previousEvent.message;
69+
70+
// If no event has a message, they were both exceptions, so bail out
71+
if (!currentMessage && !previousMessage) {
72+
return false;
73+
}
74+
75+
// If only one event has a stacktrace, but not the other one, they are not the same
76+
if ((currentMessage && !previousMessage) || (!currentMessage && previousMessage)) {
77+
return false;
78+
}
79+
80+
if (currentMessage !== previousMessage) {
81+
return false;
82+
}
83+
84+
if (!this.isSameFingerprint(currentEvent, previousEvent)) {
85+
return false;
86+
}
87+
88+
if (!this.isSameStacktrace(currentEvent, previousEvent)) {
89+
return false;
90+
}
91+
92+
return true;
93+
}
94+
95+
/** JSDoc */
96+
private getFramesFromEvent(event: SentryEvent): StackFrame[] | undefined {
97+
const exception = event.exception;
98+
99+
if (exception) {
100+
try {
101+
// @ts-ignore
102+
return exception.values[0].stacktrace.frames;
103+
} catch (_oO) {
104+
return undefined;
105+
}
106+
} else if (event.stacktrace) {
107+
return event.stacktrace.frames;
108+
} else {
109+
return undefined;
110+
}
111+
}
112+
113+
/** JSDoc */
114+
private isSameStacktrace(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean {
115+
let currentFrames = this.getFramesFromEvent(currentEvent);
116+
let previousFrames = this.getFramesFromEvent(previousEvent);
117+
118+
// If no event has a fingerprint, they are assumed to be the same
119+
if (!currentFrames && !previousFrames) {
120+
return true;
121+
}
122+
123+
// If only one event has a stacktrace, but not the other one, they are not the same
124+
if ((currentFrames && !previousFrames) || (!currentFrames && previousFrames)) {
125+
return false;
126+
}
127+
128+
currentFrames = currentFrames as StackFrame[];
129+
previousFrames = previousFrames as StackFrame[];
130+
131+
// If number of frames differ, they are not the same
132+
if (previousFrames.length !== currentFrames.length) {
133+
return false;
134+
}
135+
136+
// Otherwise, compare the two
137+
for (let i = 0; i < previousFrames.length; i++) {
138+
const frameA = previousFrames[i];
139+
const frameB = currentFrames[i];
140+
141+
if (
142+
frameA.filename !== frameB.filename ||
143+
frameA.lineno !== frameB.lineno ||
144+
frameA.colno !== frameB.colno ||
145+
frameA.function !== frameB.function
146+
) {
147+
return false;
148+
}
149+
}
150+
151+
return true;
152+
}
153+
154+
/** JSDoc */
155+
private getExceptionFromEvent(event: SentryEvent): SentryException | undefined {
156+
return event.exception && event.exception.values && event.exception.values[0];
157+
}
158+
159+
/** JSDoc */
160+
private isSameExceptionEvent(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean {
161+
const previousException = this.getExceptionFromEvent(previousEvent);
162+
const currentException = this.getExceptionFromEvent(currentEvent);
163+
164+
if (!previousException || !currentException) {
165+
return false;
166+
}
167+
168+
if (previousException.type !== currentException.type || previousException.value !== currentException.value) {
169+
return false;
170+
}
171+
172+
if (!this.isSameFingerprint(currentEvent, previousEvent)) {
173+
return false;
174+
}
175+
176+
if (!this.isSameStacktrace(currentEvent, previousEvent)) {
177+
return false;
178+
}
179+
180+
return true;
181+
}
182+
183+
/** JSDoc */
184+
private isSameFingerprint(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean {
185+
let currentFingerprint = currentEvent.fingerprint;
186+
let previousFingerprint = previousEvent.fingerprint;
187+
188+
// If no event has a fingerprint, they are assumed to be the same
189+
if (!currentFingerprint && !previousFingerprint) {
190+
return true;
191+
}
192+
193+
// If only one event has a fingerprint, but not the other one, they are not the same
194+
if ((currentFingerprint && !previousFingerprint) || (!currentFingerprint && previousFingerprint)) {
195+
return false;
196+
}
197+
198+
currentFingerprint = currentFingerprint as string[];
199+
previousFingerprint = previousFingerprint as string[];
200+
201+
// Otherwise, compare the two
202+
try {
203+
return !!(currentFingerprint.join('') === previousFingerprint.join(''));
204+
} catch (_oO) {
205+
return false;
206+
}
207+
}
208+
}

packages/browser/src/integrations/functiontostring.ts renamed to packages/core/src/integrations/functiontostring.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class FunctionToString implements Integration {
1616

1717
Function.prototype.toString = function(this: SentryWrappedFunction, ...args: any[]): string {
1818
const context = this.__sentry__ ? this.__sentry_original__ : this;
19+
// tslint:disable-next-line:no-unsafe-any
1920
return originalFunctionToString.apply(context, args);
2021
};
2122
}

packages/browser/src/integrations/inboundfilters.ts renamed to packages/core/src/integrations/inboundfilters.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
import { configureScope, logger } from '@sentry/core';
1+
import { configureScope } from '@sentry/minimal';
22
import { Integration, SentryEvent } from '@sentry/types';
33
import { isRegExp } from '@sentry/utils/is';
44
import { getEventDescription } from '@sentry/utils/misc';
5-
import { BrowserOptions } from '../backend';
5+
import { logger } from '../logger';
66

77
// "Script error." is hard coded into browsers for errors that it can't read.
88
// this is the result of a script being pulled in from an external domain and CORS.
99
const DEFAULT_IGNORE_ERRORS = [/^Script error\.?$/, /^Javascript error: Script error\.? on line 0$/];
1010

11+
/** JSDoc */
12+
interface InboundFiltersOptions {
13+
ignoreErrors?: Array<string | RegExp>;
14+
blacklistUrls?: Array<string | RegExp>;
15+
whitelistUrls?: Array<string | RegExp>;
16+
}
17+
1118
/** Inbound filters configurable by the user */
1219
export class InboundFilters implements Integration {
1320
/** JSDoc */
@@ -24,7 +31,7 @@ export class InboundFilters implements Integration {
2431
/**
2532
* @inheritDoc
2633
*/
27-
public install(options: BrowserOptions = {}): void {
34+
public install(options: InboundFiltersOptions = {}): void {
2835
this.configureOptions(options);
2936

3037
configureScope(scope => {
@@ -108,7 +115,7 @@ export class InboundFilters implements Integration {
108115
}
109116

110117
/** JSDoc */
111-
private configureOptions(options: BrowserOptions): void {
118+
private configureOptions(options: InboundFiltersOptions): void {
112119
if (options.ignoreErrors) {
113120
this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...options.ignoreErrors];
114121
}
@@ -122,13 +129,12 @@ export class InboundFilters implements Integration {
122129

123130
/** JSDoc */
124131
private getPossibleEventMessages(event: SentryEvent): string[] {
125-
const evt = event as any;
126-
127-
if (evt.message) {
128-
return [evt.message];
129-
} else if (evt.exception) {
132+
if (event.message) {
133+
return [event.message];
134+
} else if (event.exception) {
130135
try {
131-
const { type, value } = evt.exception.values[0];
136+
// tslint:disable-next-line:no-unsafe-any
137+
const { type, value } = (event as any).exception.values[0];
132138
return [`${value}`, `${type}: ${value}`];
133139
} catch (oO) {
134140
logger.error(`Cannot extract message for event ${getEventDescription(event)}`);
@@ -141,13 +147,13 @@ export class InboundFilters implements Integration {
141147

142148
/** JSDoc */
143149
private getEventFilterUrl(event: SentryEvent): string | null {
144-
const evt = event as any;
145-
146150
try {
147-
if (evt.stacktrace) {
148-
return evt.stacktrace.frames[0].filename;
149-
} else if (evt.exception) {
150-
return evt.exception.values[0].stacktrace.frames[0].filename;
151+
if (event.stacktrace) {
152+
// tslint:disable-next-line:no-unsafe-any
153+
return (event as any).stacktrace.frames[0].filename;
154+
} else if (event.exception) {
155+
// tslint:disable-next-line:no-unsafe-any
156+
return (event as any).exception.values[0].stacktrace.frames[0].filename;
151157
} else {
152158
return null;
153159
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1+
export { Dedupe } from './dedupe';
2+
export { FunctionToString } from './functiontostring';
3+
export { SDKInformation } from './sdkinformation';
4+
export { InboundFilters } from './inboundfilters';
5+
16
export { Debug } from './pluggable/debug';

0 commit comments

Comments
 (0)