Skip to content

Commit 4905a84

Browse files
kamilogorekHazAT
authored andcommitted
Instrumentation refactor (#2323)
* ref: Introduce getFunctionName util and unify its usage * test: Add test that covers onreadystatechange wrapping * ref: Extract XHR wrapping from Breadcrumbs to TryCatch * fix: Don't share same object for instrumentation handlers * ref: Remove requestComplete and use endTimestamp to determine completeness * ref: Refactor dom instrumentation * ref: Make instrumentation dependent on handlers and the part of the browser sdk itself * fix: Dont instrument same type twice
1 parent a6c0081 commit 4905a84

File tree

10 files changed

+828
-743
lines changed

10 files changed

+828
-743
lines changed

packages/browser/src/helpers.ts

Lines changed: 7 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { captureException, getCurrentHub, withScope } from '@sentry/core';
1+
import { captureException, withScope } from '@sentry/core';
22
import { Event as SentryEvent, Mechanism, Scope, WrappedFunction } from '@sentry/types';
3-
import { addExceptionMechanism, addExceptionTypeValue, htmlTreeAsString, normalize } from '@sentry/utils';
3+
import { addExceptionMechanism, addExceptionTypeValue, normalize } from '@sentry/utils';
44

5-
const debounceDuration: number = 1000;
6-
let keypressTimeout: number | undefined;
7-
let lastCapturedEvent: Event | undefined;
85
let ignoreOnError: number = 0;
96

107
/**
@@ -37,7 +34,6 @@ export function wrap(
3734
fn: WrappedFunction,
3835
options: {
3936
mechanism?: Mechanism;
40-
capture?: boolean;
4137
} = {},
4238
before?: WrappedFunction,
4339
): any {
@@ -64,15 +60,15 @@ export function wrap(
6460
}
6561

6662
const sentryWrapped: WrappedFunction = function(this: any): void {
67-
// tslint:disable-next-line:strict-type-predicates
68-
if (before && typeof before === 'function') {
69-
before.apply(this, arguments);
70-
}
71-
7263
const args = Array.prototype.slice.call(arguments);
7364

7465
// tslint:disable:no-unsafe-any
7566
try {
67+
// tslint:disable-next-line:strict-type-predicates
68+
if (before && typeof before === 'function') {
69+
before.apply(this, arguments);
70+
}
71+
7672
const wrappedArguments = args.map((arg: any) => wrap(arg, options));
7773

7874
if (fn.handleEvent) {
@@ -82,7 +78,6 @@ export function wrap(
8278
// is expected behavior and NOT indicative of a bug with sentry.javascript.
8379
return fn.handleEvent.apply(this, wrappedArguments);
8480
}
85-
8681
// Attempt to invoke user-land function
8782
// NOTE: If you are a Sentry user, and you are seeing this stack frame, it
8883
// means the sentry.javascript SDK caught an error invoking your application code. This
@@ -163,106 +158,3 @@ export function wrap(
163158

164159
return sentryWrapped;
165160
}
166-
167-
let debounceTimer: number = 0;
168-
169-
/**
170-
* Wraps addEventListener to capture UI breadcrumbs
171-
* @param eventName the event name (e.g. "click")
172-
* @returns wrapped breadcrumb events handler
173-
* @hidden
174-
*/
175-
export function breadcrumbEventHandler(eventName: string, debounce: boolean = false): (event: Event) => void {
176-
return (event: Event) => {
177-
// reset keypress timeout; e.g. triggering a 'click' after
178-
// a 'keypress' will reset the keypress debounce so that a new
179-
// set of keypresses can be recorded
180-
keypressTimeout = undefined;
181-
// It's possible this handler might trigger multiple times for the same
182-
// event (e.g. event propagation through node ancestors). Ignore if we've
183-
// already captured the event.
184-
if (!event || lastCapturedEvent === event) {
185-
return;
186-
}
187-
188-
lastCapturedEvent = event;
189-
190-
const captureBreadcrumb = () => {
191-
let target;
192-
193-
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
194-
try {
195-
target = event.target ? htmlTreeAsString(event.target as Node) : htmlTreeAsString((event as unknown) as Node);
196-
} catch (e) {
197-
target = '<unknown>';
198-
}
199-
200-
if (target.length === 0) {
201-
return;
202-
}
203-
204-
getCurrentHub().addBreadcrumb(
205-
{
206-
category: `ui.${eventName}`, // e.g. ui.click, ui.input
207-
message: target,
208-
},
209-
{
210-
event,
211-
name: eventName,
212-
},
213-
);
214-
};
215-
216-
if (debounceTimer) {
217-
clearTimeout(debounceTimer);
218-
}
219-
220-
if (debounce) {
221-
debounceTimer = setTimeout(captureBreadcrumb);
222-
} else {
223-
captureBreadcrumb();
224-
}
225-
};
226-
}
227-
228-
/**
229-
* Wraps addEventListener to capture keypress UI events
230-
* @returns wrapped keypress events handler
231-
* @hidden
232-
*/
233-
export function keypressEventHandler(): (event: Event) => void {
234-
// TODO: if somehow user switches keypress target before
235-
// debounce timeout is triggered, we will only capture
236-
// a single breadcrumb from the FIRST target (acceptable?)
237-
return (event: Event) => {
238-
let target;
239-
240-
try {
241-
target = event.target;
242-
} catch (e) {
243-
// just accessing event properties can throw an exception in some rare circumstances
244-
// see: https://github.com/getsentry/raven-js/issues/838
245-
return;
246-
}
247-
248-
const tagName = target && (target as HTMLElement).tagName;
249-
250-
// only consider keypress events on actual input elements
251-
// this will disregard keypresses targeting body (e.g. tabbing
252-
// through elements, hotkeys, etc)
253-
if (!tagName || (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !(target as HTMLElement).isContentEditable)) {
254-
return;
255-
}
256-
257-
// record first keypress in a series, but ignore subsequent
258-
// keypresses until debounce clears
259-
if (!keypressTimeout) {
260-
breadcrumbEventHandler('input')(event);
261-
}
262-
clearTimeout(keypressTimeout);
263-
264-
keypressTimeout = (setTimeout(() => {
265-
keypressTimeout = undefined;
266-
}, debounceDuration) as any) as number;
267-
};
268-
}

packages/browser/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export { BrowserOptions } from './backend';
3838
export { BrowserClient, ReportDialogOptions } from './client';
3939
export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk';
4040
export { SDK_NAME, SDK_VERSION } from './version';
41+
export { addInstrumentationHandler } from './instrument';
4142

4243
import { Integrations as CoreIntegrations } from '@sentry/core';
4344
import { getGlobalObject } from '@sentry/utils';

0 commit comments

Comments
 (0)