Skip to content

ref(browser): Extract private methods from breadcrumbs #4296

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

Merged
merged 3 commits into from
Dec 15, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 143 additions & 147 deletions packages/browser/src/integrations/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,84 +85,47 @@ export class Breadcrumbs implements Integration {
public setupOnce(): void {
if (this._options.console) {
addInstrumentationHandler({
callback: (...args) => {
this._consoleBreadcrumb(...args);
},
callback: _consoleBreadcrumb,
type: 'console',
});
}
if (this._options.dom) {
addInstrumentationHandler({
callback: (...args) => {
this._domBreadcrumb(...args);
},
callback: _domBreadcrumb(this._options.dom),
type: 'dom',
});
}
if (this._options.xhr) {
addInstrumentationHandler({
callback: (...args) => {
this._xhrBreadcrumb(...args);
},
callback: _xhrBreadcrumb,
type: 'xhr',
});
}
if (this._options.fetch) {
addInstrumentationHandler({
callback: (...args) => {
this._fetchBreadcrumb(...args);
},
callback: _fetchBreadcrumb,
type: 'fetch',
});
}
if (this._options.history) {
addInstrumentationHandler({
callback: (...args) => {
this._historyBreadcrumb(...args);
},
callback: _historyBreadcrumb,
type: 'history',
});
}
}
}

/**
* Creates breadcrumbs from console API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _consoleBreadcrumb(handlerData: { [key: string]: any }): void {
const breadcrumb = {
category: 'console',
data: {
arguments: handlerData.args,
logger: 'console',
},
level: Severity.fromString(handlerData.level),
message: safeJoin(handlerData.args, ' '),
};

if (handlerData.level === 'assert') {
if (handlerData.args[0] === false) {
breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;
breadcrumb.data.arguments = handlerData.args.slice(1);
} else {
// Don't capture a breadcrumb for passed assertions
return;
}
}

getCurrentHub().addBreadcrumb(breadcrumb, {
input: handlerData.args,
level: handlerData.level,
});
}

/**
* Creates breadcrumbs from DOM API calls
*/
/**
* A HOC that creaes a function that creates breadcrumbs from DOM API calls.
* This is a HOC so that we get access to dom options in the closure.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: string]: any }) => void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _domBreadcrumb(handlerData: { [key: string]: any }): void {
function _innerDomBreadcrumb(handlerData: { [key: string]: any }): void {
let target;
let keyAttrs = typeof this._options.dom === 'object' ? this._options.dom.serializeAttribute : undefined;
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;

if (typeof keyAttrs === 'string') {
keyAttrs = [keyAttrs];
Expand Down Expand Up @@ -194,117 +157,150 @@ export class Breadcrumbs implements Integration {
);
}

/**
* Creates breadcrumbs from XHR API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _xhrBreadcrumb(handlerData: { [key: string]: any }): void {
if (handlerData.endTimestamp) {
// We only capture complete, non-sentry requests
if (handlerData.xhr.__sentry_own_request__) {
return;
}

const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {};
return _innerDomBreadcrumb;
}

getCurrentHub().addBreadcrumb(
{
category: 'xhr',
data: {
method,
url,
status_code,
},
type: 'http',
},
{
xhr: handlerData.xhr,
input: body,
},
);
/**
* Creates breadcrumbs from console API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _consoleBreadcrumb(handlerData: { [key: string]: any }): void {
const breadcrumb = {
category: 'console',
data: {
arguments: handlerData.args,
logger: 'console',
},
level: Severity.fromString(handlerData.level),
message: safeJoin(handlerData.args, ' '),
};

if (handlerData.level === 'assert') {
if (handlerData.args[0] === false) {
breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;
breadcrumb.data.arguments = handlerData.args.slice(1);
} else {
// Don't capture a breadcrumb for passed assertions
return;
}
}

/**
* Creates breadcrumbs from fetch API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _fetchBreadcrumb(handlerData: { [key: string]: any }): void {
// We only capture complete fetch requests
if (!handlerData.endTimestamp) {
return;
}
getCurrentHub().addBreadcrumb(breadcrumb, {
input: handlerData.args,
level: handlerData.level,
});
}

if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {
// We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)
/**
* Creates breadcrumbs from XHR API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _xhrBreadcrumb(handlerData: { [key: string]: any }): void {
if (handlerData.endTimestamp) {
// We only capture complete, non-sentry requests
if (handlerData.xhr.__sentry_own_request__) {
return;
}

if (handlerData.error) {
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: handlerData.fetchData,
level: Severity.Error,
type: 'http',
},
{
data: handlerData.error,
input: handlerData.args,
},
);
} else {
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: {
...handlerData.fetchData,
status_code: handlerData.response.status,
},
type: 'http',
},
{
input: handlerData.args,
response: handlerData.response,
const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {};

getCurrentHub().addBreadcrumb(
{
category: 'xhr',
data: {
method,
url,
status_code,
},
);
}
}
type: 'http',
},
{
xhr: handlerData.xhr,
input: body,
},
);

/**
* Creates breadcrumbs from history API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _historyBreadcrumb(handlerData: { [key: string]: any }): void {
const global = getGlobalObject<Window>();
let from = handlerData.from;
let to = handlerData.to;
const parsedLoc = parseUrl(global.location.href);
let parsedFrom = parseUrl(from);
const parsedTo = parseUrl(to);
return;
}
}

// Initial pushState doesn't provide `from` information
if (!parsedFrom.path) {
parsedFrom = parsedLoc;
}
/**
* Creates breadcrumbs from fetch API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _fetchBreadcrumb(handlerData: { [key: string]: any }): void {
// We only capture complete fetch requests
if (!handlerData.endTimestamp) {
return;
}

// Use only the path component of the URL if the URL matches the current
// document (almost all the time when using pushState)
if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {
to = parsedTo.relative;
}
if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {
from = parsedFrom.relative;
}
if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {
// We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)
return;
}

getCurrentHub().addBreadcrumb({
category: 'navigation',
data: {
from,
to,
if (handlerData.error) {
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: handlerData.fetchData,
level: Severity.Error,
type: 'http',
},
{
data: handlerData.error,
input: handlerData.args,
},
});
);
} else {
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: {
...handlerData.fetchData,
status_code: handlerData.response.status,
},
type: 'http',
},
{
input: handlerData.args,
response: handlerData.response,
},
);
}
}

/**
* Creates breadcrumbs from history API calls
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _historyBreadcrumb(handlerData: { [key: string]: any }): void {
const global = getGlobalObject<Window>();
let from = handlerData.from;
let to = handlerData.to;
const parsedLoc = parseUrl(global.location.href);
let parsedFrom = parseUrl(from);
const parsedTo = parseUrl(to);

// Initial pushState doesn't provide `from` information
if (!parsedFrom.path) {
parsedFrom = parsedLoc;
}

// Use only the path component of the URL if the URL matches the current
// document (almost all the time when using pushState)
if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {
to = parsedTo.relative;
}
if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {
from = parsedFrom.relative;
}

getCurrentHub().addBreadcrumb({
category: 'navigation',
data: {
from,
to,
},
});
}