123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- import { getClient } from '@sentry/core';
- import { addExceptionMechanism, resolvedSyncPromise, isErrorEvent, isDOMError, isDOMException, addExceptionTypeValue, isError, isPlainObject, isEvent, isParameterizedString, normalizeToSize, extractExceptionKeysForMessage } from '@sentry/utils';
- /**
- * This function creates an exception from a JavaScript Error
- */
- function exceptionFromError(stackParser, ex) {
- // Get the frames first since Opera can lose the stack if we touch anything else first
- const frames = parseStackFrames(stackParser, ex);
- const exception = {
- type: ex && ex.name,
- value: extractMessage(ex),
- };
- if (frames.length) {
- exception.stacktrace = { frames };
- }
- if (exception.type === undefined && exception.value === '') {
- exception.value = 'Unrecoverable error caught';
- }
- return exception;
- }
- /**
- * @hidden
- */
- function eventFromPlainObject(
- stackParser,
- exception,
- syntheticException,
- isUnhandledRejection,
- ) {
- const client = getClient();
- const normalizeDepth = client && client.getOptions().normalizeDepth;
- const event = {
- exception: {
- values: [
- {
- type: isEvent(exception) ? exception.constructor.name : isUnhandledRejection ? 'UnhandledRejection' : 'Error',
- value: getNonErrorObjectExceptionValue(exception, { isUnhandledRejection }),
- },
- ],
- },
- extra: {
- __serialized__: normalizeToSize(exception, normalizeDepth),
- },
- };
- if (syntheticException) {
- const frames = parseStackFrames(stackParser, syntheticException);
- if (frames.length) {
- // event.exception.values[0] has been set above
- (event.exception ).values[0].stacktrace = { frames };
- }
- }
- return event;
- }
- /**
- * @hidden
- */
- function eventFromError(stackParser, ex) {
- return {
- exception: {
- values: [exceptionFromError(stackParser, ex)],
- },
- };
- }
- /** Parses stack frames from an error */
- function parseStackFrames(
- stackParser,
- ex,
- ) {
- // Access and store the stacktrace property before doing ANYTHING
- // else to it because Opera is not very good at providing it
- // reliably in other circumstances.
- const stacktrace = ex.stacktrace || ex.stack || '';
- const popSize = getPopSize(ex);
- try {
- return stackParser(stacktrace, popSize);
- } catch (e) {
- // no-empty
- }
- return [];
- }
- // Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
- const reactMinifiedRegexp = /Minified React error #\d+;/i;
- function getPopSize(ex) {
- if (ex) {
- if (typeof ex.framesToPop === 'number') {
- return ex.framesToPop;
- }
- if (reactMinifiedRegexp.test(ex.message)) {
- return 1;
- }
- }
- return 0;
- }
- /**
- * There are cases where stacktrace.message is an Event object
- * https://github.com/getsentry/sentry-javascript/issues/1949
- * In this specific case we try to extract stacktrace.message.error.message
- */
- function extractMessage(ex) {
- const message = ex && ex.message;
- if (!message) {
- return 'No error message';
- }
- if (message.error && typeof message.error.message === 'string') {
- return message.error.message;
- }
- return message;
- }
- /**
- * Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`.
- * @hidden
- */
- function eventFromException(
- stackParser,
- exception,
- hint,
- attachStacktrace,
- ) {
- const syntheticException = (hint && hint.syntheticException) || undefined;
- const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace);
- addExceptionMechanism(event); // defaults to { type: 'generic', handled: true }
- event.level = 'error';
- if (hint && hint.event_id) {
- event.event_id = hint.event_id;
- }
- return resolvedSyncPromise(event);
- }
- /**
- * Builds and Event from a Message
- * @hidden
- */
- function eventFromMessage(
- stackParser,
- message,
- // eslint-disable-next-line deprecation/deprecation
- level = 'info',
- hint,
- attachStacktrace,
- ) {
- const syntheticException = (hint && hint.syntheticException) || undefined;
- const event = eventFromString(stackParser, message, syntheticException, attachStacktrace);
- event.level = level;
- if (hint && hint.event_id) {
- event.event_id = hint.event_id;
- }
- return resolvedSyncPromise(event);
- }
- /**
- * @hidden
- */
- function eventFromUnknownInput(
- stackParser,
- exception,
- syntheticException,
- attachStacktrace,
- isUnhandledRejection,
- ) {
- let event;
- if (isErrorEvent(exception ) && (exception ).error) {
- // If it is an ErrorEvent with `error` property, extract it to get actual Error
- const errorEvent = exception ;
- return eventFromError(stackParser, errorEvent.error );
- }
- // If it is a `DOMError` (which is a legacy API, but still supported in some browsers) then we just extract the name
- // and message, as it doesn't provide anything else. According to the spec, all `DOMExceptions` should also be
- // `Error`s, but that's not the case in IE11, so in that case we treat it the same as we do a `DOMError`.
- //
- // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
- // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
- // https://webidl.spec.whatwg.org/#es-DOMException-specialness
- if (isDOMError(exception) || isDOMException(exception )) {
- const domException = exception ;
- if ('stack' in (exception )) {
- event = eventFromError(stackParser, exception );
- } else {
- const name = domException.name || (isDOMError(domException) ? 'DOMError' : 'DOMException');
- const message = domException.message ? `${name}: ${domException.message}` : name;
- event = eventFromString(stackParser, message, syntheticException, attachStacktrace);
- addExceptionTypeValue(event, message);
- }
- if ('code' in domException) {
- // eslint-disable-next-line deprecation/deprecation
- event.tags = { ...event.tags, 'DOMException.code': `${domException.code}` };
- }
- return event;
- }
- if (isError(exception)) {
- // we have a real Error object, do nothing
- return eventFromError(stackParser, exception);
- }
- if (isPlainObject(exception) || isEvent(exception)) {
- // If it's a plain object or an instance of `Event` (the built-in JS kind, not this SDK's `Event` type), serialize
- // it manually. This will allow us to group events based on top-level keys which is much better than creating a new
- // group on any key/value change.
- const objectException = exception ;
- event = eventFromPlainObject(stackParser, objectException, syntheticException, isUnhandledRejection);
- addExceptionMechanism(event, {
- synthetic: true,
- });
- return event;
- }
- // If none of previous checks were valid, then it means that it's not:
- // - an instance of DOMError
- // - an instance of DOMException
- // - an instance of Event
- // - an instance of Error
- // - a valid ErrorEvent (one with an error property)
- // - a plain Object
- //
- // So bail out and capture it as a simple message:
- event = eventFromString(stackParser, exception , syntheticException, attachStacktrace);
- addExceptionTypeValue(event, `${exception}`, undefined);
- addExceptionMechanism(event, {
- synthetic: true,
- });
- return event;
- }
- /**
- * @hidden
- */
- function eventFromString(
- stackParser,
- message,
- syntheticException,
- attachStacktrace,
- ) {
- const event = {};
- if (attachStacktrace && syntheticException) {
- const frames = parseStackFrames(stackParser, syntheticException);
- if (frames.length) {
- event.exception = {
- values: [{ value: message, stacktrace: { frames } }],
- };
- }
- }
- if (isParameterizedString(message)) {
- const { __sentry_template_string__, __sentry_template_values__ } = message;
- event.logentry = {
- message: __sentry_template_string__,
- params: __sentry_template_values__,
- };
- return event;
- }
- event.message = message;
- return event;
- }
- function getNonErrorObjectExceptionValue(
- exception,
- { isUnhandledRejection },
- ) {
- const keys = extractExceptionKeysForMessage(exception);
- const captureType = isUnhandledRejection ? 'promise rejection' : 'exception';
- // Some ErrorEvent instances do not have an `error` property, which is why they are not handled before
- // We still want to try to get a decent message for these cases
- if (isErrorEvent(exception)) {
- return `Event \`ErrorEvent\` captured as ${captureType} with message \`${exception.message}\``;
- }
- if (isEvent(exception)) {
- const className = getObjectClassName(exception);
- return `Event \`${className}\` (type=${exception.type}) captured as ${captureType}`;
- }
- return `Object captured as ${captureType} with keys: ${keys}`;
- }
- function getObjectClassName(obj) {
- try {
- const prototype = Object.getPrototypeOf(obj);
- return prototype ? prototype.constructor.name : undefined;
- } catch (e) {
- // ignore errors here
- }
- }
- export { eventFromError, eventFromException, eventFromMessage, eventFromPlainObject, eventFromString, eventFromUnknownInput, exceptionFromError, parseStackFrames };
- //# sourceMappingURL=eventbuilder.js.map
|