UNPKG

@sentry/core

Version:
173 lines (148 loc) 5.59 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const STACKTRACE_FRAME_LIMIT = 50; const UNKNOWN_FUNCTION = '?'; // Used to sanitize webpack (error: *) wrapped stack errors const WEBPACK_ERROR_REGEXP = /\(error: (.*)\)/; const STRIP_FRAME_REGEXP = /captureMessage|captureException/; /** * Creates a stack parser with the supplied line parsers * * StackFrames are returned in the correct order for Sentry Exception * frames and with Sentry SDK internal frames removed from the top and bottom * */ function createStackParser(...parsers) { const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]); return (stack, skipFirstLines = 0, framesToPop = 0) => { const frames = []; const lines = stack.split('\n'); for (let i = skipFirstLines; i < lines.length; i++) { const line = lines[i] ; // Ignore lines over 1kb as they are unlikely to be stack frames. // Many of the regular expressions use backtracking which results in run time that increases exponentially with // input size. Huge strings can result in hangs/Denial of Service: // https://github.com/getsentry/sentry-javascript/issues/2286 if (line.length > 1024) { continue; } // https://github.com/getsentry/sentry-javascript/issues/5459 // Remove webpack (error: *) wrappers const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line; // https://github.com/getsentry/sentry-javascript/issues/7813 // Skip Error: lines if (cleanedLine.match(/\S*Error: /)) { continue; } for (const parser of sortedParsers) { const frame = parser(cleanedLine); if (frame) { frames.push(frame); break; } } if (frames.length >= STACKTRACE_FRAME_LIMIT + framesToPop) { break; } } return stripSentryFramesAndReverse(frames.slice(framesToPop)); }; } /** * Gets a stack parser implementation from Options.stackParser * @see Options * * If options contains an array of line parsers, it is converted into a parser */ function stackParserFromStackParserOptions(stackParser) { if (Array.isArray(stackParser)) { return createStackParser(...stackParser); } return stackParser; } /** * Removes Sentry frames from the top and bottom of the stack if present and enforces a limit of max number of frames. * Assumes stack input is ordered from top to bottom and returns the reverse representation so call site of the * function that caused the crash is the last frame in the array. * @hidden */ function stripSentryFramesAndReverse(stack) { if (!stack.length) { return []; } const localStack = Array.from(stack); // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call) if (/sentryWrapped/.test(getLastStackFrame(localStack).function || '')) { localStack.pop(); } // Reversing in the middle of the procedure allows us to just pop the values off the stack localStack.reverse(); // If stack ends with one of our internal API calls, remove it (ends, meaning it's the bottom of the stack - aka top-most call) if (STRIP_FRAME_REGEXP.test(getLastStackFrame(localStack).function || '')) { localStack.pop(); // When using synthetic events, we will have a 2 levels deep stack, as `new Error('Sentry syntheticException')` // is produced within the scope itself, making it: // // Sentry.captureException() // scope.captureException() // // instead of just the top `Sentry` call itself. // This forces us to possibly strip an additional frame in the exact same was as above. if (STRIP_FRAME_REGEXP.test(getLastStackFrame(localStack).function || '')) { localStack.pop(); } } return localStack.slice(0, STACKTRACE_FRAME_LIMIT).map(frame => ({ ...frame, filename: frame.filename || getLastStackFrame(localStack).filename, function: frame.function || UNKNOWN_FUNCTION, })); } function getLastStackFrame(arr) { return arr[arr.length - 1] || {}; } const defaultFunctionName = '<anonymous>'; /** * Safely extract function name from itself */ function getFunctionName(fn) { try { if (!fn || typeof fn !== 'function') { return defaultFunctionName; } return fn.name || defaultFunctionName; } catch (e) { // Just accessing custom props in some Selenium environments // can cause a "Permission denied" exception (see raven-js#495). return defaultFunctionName; } } /** * Get's stack frames from an event without needing to check for undefined properties. */ function getFramesFromEvent(event) { const exception = event.exception; if (exception) { const frames = []; try { // @ts-expect-error Object could be undefined exception.values.forEach(value => { // @ts-expect-error Value could be undefined if (value.stacktrace.frames) { // @ts-expect-error Value could be undefined frames.push(...value.stacktrace.frames); } }); return frames; } catch (_oO) { return undefined; } } return undefined; } exports.UNKNOWN_FUNCTION = UNKNOWN_FUNCTION; exports.createStackParser = createStackParser; exports.getFramesFromEvent = getFramesFromEvent; exports.getFunctionName = getFunctionName; exports.stackParserFromStackParserOptions = stackParserFromStackParserOptions; exports.stripSentryFramesAndReverse = stripSentryFramesAndReverse; //# sourceMappingURL=stacktrace.js.map