@sentry/browser
Version:
Official Sentry SDK for browsers
248 lines (245 loc) • 7.59 kB
JavaScript
import { defineIntegration, addConsoleInstrumentationHandler, addFetchInstrumentationHandler, getClient, safeJoin, severityLevelFromString, addBreadcrumb, debug, getComponentName, getBreadcrumbLogLevelFromHttpStatusCode, parseUrl, getEventDescription } from '@sentry/core/browser';
import { addClickKeypressInstrumentationHandler, addXhrInstrumentationHandler, addHistoryInstrumentationHandler, htmlTreeAsString, SENTRY_XHR_DATA_KEY } from '@sentry-internal/browser-utils';
import { DEBUG_BUILD } from '../debug-build.js';
import { WINDOW } from '../helpers.js';
const MAX_ALLOWED_STRING_LENGTH = 1024;
const INTEGRATION_NAME = "Breadcrumbs";
const _breadcrumbsIntegration = ((options = {}) => {
const _options = {
console: true,
dom: true,
fetch: true,
history: true,
sentry: true,
xhr: true,
...options
};
return {
name: INTEGRATION_NAME,
setup(client) {
if (_options.console) {
addConsoleInstrumentationHandler(_getConsoleBreadcrumbHandler(client));
}
if (_options.dom) {
addClickKeypressInstrumentationHandler(_getDomBreadcrumbHandler(client, _options.dom));
}
if (_options.xhr) {
addXhrInstrumentationHandler(_getXhrBreadcrumbHandler(client));
}
if (_options.fetch) {
addFetchInstrumentationHandler(_getFetchBreadcrumbHandler(client));
}
if (_options.history) {
addHistoryInstrumentationHandler(_getHistoryBreadcrumbHandler(client));
}
if (_options.sentry) {
client.on("beforeSendEvent", _getSentryBreadcrumbHandler(client));
}
}
};
});
const breadcrumbsIntegration = defineIntegration(_breadcrumbsIntegration);
function _getSentryBreadcrumbHandler(client) {
return function addSentryBreadcrumb(event) {
if (getClient() !== client) {
return;
}
addBreadcrumb(
{
category: `sentry.${event.type === "transaction" ? "transaction" : "event"}`,
event_id: event.event_id,
level: event.level,
message: getEventDescription(event)
},
{
event
}
);
};
}
function _getDomBreadcrumbHandler(client, dom) {
return function _innerDomBreadcrumb(handlerData) {
if (getClient() !== client) {
return;
}
let target;
let componentName;
let keyAttrs = typeof dom === "object" ? dom.serializeAttribute : void 0;
let maxStringLength = typeof dom === "object" && typeof dom.maxStringLength === "number" ? dom.maxStringLength : void 0;
if (maxStringLength && maxStringLength > MAX_ALLOWED_STRING_LENGTH) {
DEBUG_BUILD && debug.warn(
`\`dom.maxStringLength\` cannot exceed ${MAX_ALLOWED_STRING_LENGTH}, but a value of ${maxStringLength} was configured. Sentry will use ${MAX_ALLOWED_STRING_LENGTH} instead.`
);
maxStringLength = MAX_ALLOWED_STRING_LENGTH;
}
if (typeof keyAttrs === "string") {
keyAttrs = [keyAttrs];
}
try {
const event = handlerData.event;
const element = _isEvent(event) ? event.target : event;
target = htmlTreeAsString(element, { keyAttrs, maxStringLength });
componentName = getComponentName(element);
} catch {
target = "<unknown>";
}
if (target.length === 0) {
return;
}
const breadcrumb = {
category: `ui.${handlerData.name}`,
message: target
};
if (componentName) {
breadcrumb.data = { "ui.component_name": componentName };
}
addBreadcrumb(breadcrumb, {
event: handlerData.event,
name: handlerData.name,
global: handlerData.global
});
};
}
function _getConsoleBreadcrumbHandler(client) {
return function _consoleBreadcrumb(handlerData) {
if (getClient() !== client) {
return;
}
const breadcrumb = {
category: "console",
data: {
arguments: handlerData.args,
logger: "console"
},
level: severityLevelFromString(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 {
return;
}
}
addBreadcrumb(breadcrumb, {
input: handlerData.args,
level: handlerData.level
});
};
}
function _getXhrBreadcrumbHandler(client) {
return function _xhrBreadcrumb(handlerData) {
if (getClient() !== client) {
return;
}
const { startTimestamp, endTimestamp } = handlerData;
const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
if (!startTimestamp || !endTimestamp || !sentryXhrData) {
return;
}
const { method, url, status_code, body } = sentryXhrData;
const data = {
method,
url,
status_code
};
const hint = {
xhr: handlerData.xhr,
input: body,
startTimestamp,
endTimestamp
};
const breadcrumb = {
category: "xhr",
data,
type: "http",
level: getBreadcrumbLogLevelFromHttpStatusCode(status_code)
};
client.emit("beforeOutgoingRequestBreadcrumb", breadcrumb, hint);
addBreadcrumb(breadcrumb, hint);
};
}
function _getFetchBreadcrumbHandler(client) {
return function _fetchBreadcrumb(handlerData) {
if (getClient() !== client) {
return;
}
const { startTimestamp, endTimestamp } = handlerData;
if (!endTimestamp) {
return;
}
if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === "POST") {
return;
}
if (handlerData.error) {
const hint = {
data: handlerData.error,
input: handlerData.args,
startTimestamp,
endTimestamp
};
const breadcrumb = {
category: "fetch",
data: handlerData.fetchData,
level: "error",
type: "http"
};
client.emit("beforeOutgoingRequestBreadcrumb", breadcrumb, hint);
addBreadcrumb(breadcrumb, hint);
} else {
const response = handlerData.response;
const data = {
...handlerData.fetchData,
status_code: response?.status
};
const hint = {
input: handlerData.args,
response,
startTimestamp,
endTimestamp
};
const breadcrumb = {
category: "fetch",
data,
type: "http",
level: getBreadcrumbLogLevelFromHttpStatusCode(data.status_code)
};
client.emit("beforeOutgoingRequestBreadcrumb", breadcrumb, hint);
addBreadcrumb(breadcrumb, hint);
}
};
}
function _getHistoryBreadcrumbHandler(client) {
return function _historyBreadcrumb(handlerData) {
if (getClient() !== client) {
return;
}
let from = handlerData.from;
let to = handlerData.to;
const parsedLoc = parseUrl(WINDOW.location.href);
let parsedFrom = from ? parseUrl(from) : void 0;
const parsedTo = parseUrl(to);
if (!parsedFrom?.path) {
parsedFrom = parsedLoc;
}
if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {
to = parsedTo.relative;
}
if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {
from = parsedFrom.relative;
}
addBreadcrumb({
category: "navigation",
data: {
from,
to
}
});
};
}
function _isEvent(event) {
return !!event && !!event.target;
}
export { breadcrumbsIntegration };
//# sourceMappingURL=breadcrumbs.js.map