stacktrace.js
1 import { node } from './node-stack-trace.js'; 2 3 const STACKTRACE_FRAME_LIMIT = 50; 4 // Used to sanitize webpack (error: *) wrapped stack errors 5 const WEBPACK_ERROR_REGEXP = /\(error: (.*)\)/; 6 7 /** 8 * Creates a stack parser with the supplied line parsers 9 * 10 * StackFrames are returned in the correct order for Sentry Exception 11 * frames and with Sentry SDK internal frames removed from the top and bottom 12 * 13 */ 14 function createStackParser(...parsers) { 15 const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]); 16 17 return (stack, skipFirst = 0) => { 18 const frames = []; 19 const lines = stack.split('\n'); 20 21 for (let i = skipFirst; i < lines.length; i++) { 22 const line = lines[i]; 23 // Ignore lines over 1kb as they are unlikely to be stack frames. 24 // Many of the regular expressions use backtracking which results in run time that increases exponentially with 25 // input size. Huge strings can result in hangs/Denial of Service: 26 // https://github.com/getsentry/sentry-javascript/issues/2286 27 if (line.length > 1024) { 28 continue; 29 } 30 31 // https://github.com/getsentry/sentry-javascript/issues/5459 32 // Remove webpack (error: *) wrappers 33 const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line; 34 35 // https://github.com/getsentry/sentry-javascript/issues/7813 36 // Skip Error: lines 37 if (cleanedLine.match(/\S*Error: /)) { 38 continue; 39 } 40 41 for (const parser of sortedParsers) { 42 const frame = parser(cleanedLine); 43 44 if (frame) { 45 frames.push(frame); 46 break; 47 } 48 } 49 50 if (frames.length >= STACKTRACE_FRAME_LIMIT) { 51 break; 52 } 53 } 54 55 return stripSentryFramesAndReverse(frames); 56 }; 57 } 58 59 /** 60 * Gets a stack parser implementation from Options.stackParser 61 * @see Options 62 * 63 * If options contains an array of line parsers, it is converted into a parser 64 */ 65 function stackParserFromStackParserOptions(stackParser) { 66 if (Array.isArray(stackParser)) { 67 return createStackParser(...stackParser); 68 } 69 return stackParser; 70 } 71 72 /** 73 * Removes Sentry frames from the top and bottom of the stack if present and enforces a limit of max number of frames. 74 * Assumes stack input is ordered from top to bottom and returns the reverse representation so call site of the 75 * function that caused the crash is the last frame in the array. 76 * @hidden 77 */ 78 function stripSentryFramesAndReverse(stack) { 79 if (!stack.length) { 80 return []; 81 } 82 83 const localStack = stack.slice(0, STACKTRACE_FRAME_LIMIT); 84 85 const lastFrameFunction = localStack[localStack.length - 1].function; 86 // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call) 87 if (lastFrameFunction && /sentryWrapped/.test(lastFrameFunction)) { 88 localStack.pop(); 89 } 90 91 // Reversing in the middle of the procedure allows us to just pop the values off the stack 92 localStack.reverse(); 93 94 const firstFrameFunction = localStack[localStack.length - 1].function; 95 // 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) 96 if (firstFrameFunction && /captureMessage|captureException/.test(firstFrameFunction)) { 97 localStack.pop(); 98 } 99 100 return localStack.map(frame => ({ 101 ...frame, 102 filename: frame.filename || localStack[localStack.length - 1].filename, 103 function: frame.function || '?', 104 })); 105 } 106 107 const defaultFunctionName = '<anonymous>'; 108 109 /** 110 * Safely extract function name from itself 111 */ 112 function getFunctionName(fn) { 113 try { 114 if (!fn || typeof fn !== 'function') { 115 return defaultFunctionName; 116 } 117 return fn.name || defaultFunctionName; 118 } catch (e) { 119 // Just accessing custom props in some Selenium environments 120 // can cause a "Permission denied" exception (see raven-js#495). 121 return defaultFunctionName; 122 } 123 } 124 125 /** 126 * Node.js stack line parser 127 * 128 * This is in @sentry/utils so it can be used from the Electron SDK in the browser for when `nodeIntegration == true`. 129 * This allows it to be used without referencing or importing any node specific code which causes bundlers to complain 130 */ 131 function nodeStackLineParser(getModule) { 132 return [90, node(getModule)]; 133 } 134 135 export { createStackParser, getFunctionName, nodeStackLineParser, stackParserFromStackParserOptions, stripSentryFramesAndReverse }; 136 //# sourceMappingURL=stacktrace.js.map