eventbuilder.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const core = require('@sentry/core');
  3. const utils = require('@sentry/utils');
  4. /**
  5. * This function creates an exception from a JavaScript Error
  6. */
  7. function exceptionFromError(stackParser, ex) {
  8. // Get the frames first since Opera can lose the stack if we touch anything else first
  9. const frames = parseStackFrames(stackParser, ex);
  10. const exception = {
  11. type: ex && ex.name,
  12. value: extractMessage(ex),
  13. };
  14. if (frames.length) {
  15. exception.stacktrace = { frames };
  16. }
  17. if (exception.type === undefined && exception.value === '') {
  18. exception.value = 'Unrecoverable error caught';
  19. }
  20. return exception;
  21. }
  22. /**
  23. * @hidden
  24. */
  25. function eventFromPlainObject(
  26. stackParser,
  27. exception,
  28. syntheticException,
  29. isUnhandledRejection,
  30. ) {
  31. const client = core.getClient();
  32. const normalizeDepth = client && client.getOptions().normalizeDepth;
  33. const event = {
  34. exception: {
  35. values: [
  36. {
  37. type: utils.isEvent(exception) ? exception.constructor.name : isUnhandledRejection ? 'UnhandledRejection' : 'Error',
  38. value: getNonErrorObjectExceptionValue(exception, { isUnhandledRejection }),
  39. },
  40. ],
  41. },
  42. extra: {
  43. __serialized__: utils.normalizeToSize(exception, normalizeDepth),
  44. },
  45. };
  46. if (syntheticException) {
  47. const frames = parseStackFrames(stackParser, syntheticException);
  48. if (frames.length) {
  49. // event.exception.values[0] has been set above
  50. (event.exception ).values[0].stacktrace = { frames };
  51. }
  52. }
  53. return event;
  54. }
  55. /**
  56. * @hidden
  57. */
  58. function eventFromError(stackParser, ex) {
  59. return {
  60. exception: {
  61. values: [exceptionFromError(stackParser, ex)],
  62. },
  63. };
  64. }
  65. /** Parses stack frames from an error */
  66. function parseStackFrames(
  67. stackParser,
  68. ex,
  69. ) {
  70. // Access and store the stacktrace property before doing ANYTHING
  71. // else to it because Opera is not very good at providing it
  72. // reliably in other circumstances.
  73. const stacktrace = ex.stacktrace || ex.stack || '';
  74. const popSize = getPopSize(ex);
  75. try {
  76. return stackParser(stacktrace, popSize);
  77. } catch (e) {
  78. // no-empty
  79. }
  80. return [];
  81. }
  82. // Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
  83. const reactMinifiedRegexp = /Minified React error #\d+;/i;
  84. function getPopSize(ex) {
  85. if (ex) {
  86. if (typeof ex.framesToPop === 'number') {
  87. return ex.framesToPop;
  88. }
  89. if (reactMinifiedRegexp.test(ex.message)) {
  90. return 1;
  91. }
  92. }
  93. return 0;
  94. }
  95. /**
  96. * There are cases where stacktrace.message is an Event object
  97. * https://github.com/getsentry/sentry-javascript/issues/1949
  98. * In this specific case we try to extract stacktrace.message.error.message
  99. */
  100. function extractMessage(ex) {
  101. const message = ex && ex.message;
  102. if (!message) {
  103. return 'No error message';
  104. }
  105. if (message.error && typeof message.error.message === 'string') {
  106. return message.error.message;
  107. }
  108. return message;
  109. }
  110. /**
  111. * Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`.
  112. * @hidden
  113. */
  114. function eventFromException(
  115. stackParser,
  116. exception,
  117. hint,
  118. attachStacktrace,
  119. ) {
  120. const syntheticException = (hint && hint.syntheticException) || undefined;
  121. const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace);
  122. utils.addExceptionMechanism(event); // defaults to { type: 'generic', handled: true }
  123. event.level = 'error';
  124. if (hint && hint.event_id) {
  125. event.event_id = hint.event_id;
  126. }
  127. return utils.resolvedSyncPromise(event);
  128. }
  129. /**
  130. * Builds and Event from a Message
  131. * @hidden
  132. */
  133. function eventFromMessage(
  134. stackParser,
  135. message,
  136. // eslint-disable-next-line deprecation/deprecation
  137. level = 'info',
  138. hint,
  139. attachStacktrace,
  140. ) {
  141. const syntheticException = (hint && hint.syntheticException) || undefined;
  142. const event = eventFromString(stackParser, message, syntheticException, attachStacktrace);
  143. event.level = level;
  144. if (hint && hint.event_id) {
  145. event.event_id = hint.event_id;
  146. }
  147. return utils.resolvedSyncPromise(event);
  148. }
  149. /**
  150. * @hidden
  151. */
  152. function eventFromUnknownInput(
  153. stackParser,
  154. exception,
  155. syntheticException,
  156. attachStacktrace,
  157. isUnhandledRejection,
  158. ) {
  159. let event;
  160. if (utils.isErrorEvent(exception ) && (exception ).error) {
  161. // If it is an ErrorEvent with `error` property, extract it to get actual Error
  162. const errorEvent = exception ;
  163. return eventFromError(stackParser, errorEvent.error );
  164. }
  165. // If it is a `DOMError` (which is a legacy API, but still supported in some browsers) then we just extract the name
  166. // and message, as it doesn't provide anything else. According to the spec, all `DOMExceptions` should also be
  167. // `Error`s, but that's not the case in IE11, so in that case we treat it the same as we do a `DOMError`.
  168. //
  169. // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
  170. // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
  171. // https://webidl.spec.whatwg.org/#es-DOMException-specialness
  172. if (utils.isDOMError(exception) || utils.isDOMException(exception )) {
  173. const domException = exception ;
  174. if ('stack' in (exception )) {
  175. event = eventFromError(stackParser, exception );
  176. } else {
  177. const name = domException.name || (utils.isDOMError(domException) ? 'DOMError' : 'DOMException');
  178. const message = domException.message ? `${name}: ${domException.message}` : name;
  179. event = eventFromString(stackParser, message, syntheticException, attachStacktrace);
  180. utils.addExceptionTypeValue(event, message);
  181. }
  182. if ('code' in domException) {
  183. // eslint-disable-next-line deprecation/deprecation
  184. event.tags = { ...event.tags, 'DOMException.code': `${domException.code}` };
  185. }
  186. return event;
  187. }
  188. if (utils.isError(exception)) {
  189. // we have a real Error object, do nothing
  190. return eventFromError(stackParser, exception);
  191. }
  192. if (utils.isPlainObject(exception) || utils.isEvent(exception)) {
  193. // If it's a plain object or an instance of `Event` (the built-in JS kind, not this SDK's `Event` type), serialize
  194. // it manually. This will allow us to group events based on top-level keys which is much better than creating a new
  195. // group on any key/value change.
  196. const objectException = exception ;
  197. event = eventFromPlainObject(stackParser, objectException, syntheticException, isUnhandledRejection);
  198. utils.addExceptionMechanism(event, {
  199. synthetic: true,
  200. });
  201. return event;
  202. }
  203. // If none of previous checks were valid, then it means that it's not:
  204. // - an instance of DOMError
  205. // - an instance of DOMException
  206. // - an instance of Event
  207. // - an instance of Error
  208. // - a valid ErrorEvent (one with an error property)
  209. // - a plain Object
  210. //
  211. // So bail out and capture it as a simple message:
  212. event = eventFromString(stackParser, exception , syntheticException, attachStacktrace);
  213. utils.addExceptionTypeValue(event, `${exception}`, undefined);
  214. utils.addExceptionMechanism(event, {
  215. synthetic: true,
  216. });
  217. return event;
  218. }
  219. /**
  220. * @hidden
  221. */
  222. function eventFromString(
  223. stackParser,
  224. message,
  225. syntheticException,
  226. attachStacktrace,
  227. ) {
  228. const event = {};
  229. if (attachStacktrace && syntheticException) {
  230. const frames = parseStackFrames(stackParser, syntheticException);
  231. if (frames.length) {
  232. event.exception = {
  233. values: [{ value: message, stacktrace: { frames } }],
  234. };
  235. }
  236. }
  237. if (utils.isParameterizedString(message)) {
  238. const { __sentry_template_string__, __sentry_template_values__ } = message;
  239. event.logentry = {
  240. message: __sentry_template_string__,
  241. params: __sentry_template_values__,
  242. };
  243. return event;
  244. }
  245. event.message = message;
  246. return event;
  247. }
  248. function getNonErrorObjectExceptionValue(
  249. exception,
  250. { isUnhandledRejection },
  251. ) {
  252. const keys = utils.extractExceptionKeysForMessage(exception);
  253. const captureType = isUnhandledRejection ? 'promise rejection' : 'exception';
  254. // Some ErrorEvent instances do not have an `error` property, which is why they are not handled before
  255. // We still want to try to get a decent message for these cases
  256. if (utils.isErrorEvent(exception)) {
  257. return `Event \`ErrorEvent\` captured as ${captureType} with message \`${exception.message}\``;
  258. }
  259. if (utils.isEvent(exception)) {
  260. const className = getObjectClassName(exception);
  261. return `Event \`${className}\` (type=${exception.type}) captured as ${captureType}`;
  262. }
  263. return `Object captured as ${captureType} with keys: ${keys}`;
  264. }
  265. function getObjectClassName(obj) {
  266. try {
  267. const prototype = Object.getPrototypeOf(obj);
  268. return prototype ? prototype.constructor.name : undefined;
  269. } catch (e) {
  270. // ignore errors here
  271. }
  272. }
  273. exports.eventFromError = eventFromError;
  274. exports.eventFromException = eventFromException;
  275. exports.eventFromMessage = eventFromMessage;
  276. exports.eventFromPlainObject = eventFromPlainObject;
  277. exports.eventFromString = eventFromString;
  278. exports.eventFromUnknownInput = eventFromUnknownInput;
  279. exports.exceptionFromError = exceptionFromError;
  280. exports.parseStackFrames = parseStackFrames;
  281. //# sourceMappingURL=eventbuilder.js.map