UNPKG

@sentry/browser

Version:
248 lines (245 loc) 7.59 kB
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