From 847b05b2232d684db4eda8e175b3d5b286c2b5b8 Mon Sep 17 00:00:00 2001 From: Jackson Weber Date: Tue, 24 Mar 2026 14:49:54 -0700 Subject: [PATCH] Update stack frame parsing for exceptions. --- src/logs/exceptions.ts | 11 +++++- test/unitTests/logs/exceptions.tests.ts | 48 ++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/logs/exceptions.ts b/src/logs/exceptions.ts index d98c8045d..54c7032ed 100644 --- a/src/logs/exceptions.ts +++ b/src/logs/exceptions.ts @@ -91,6 +91,8 @@ export class AutoCollectExceptions { // regex to match stack frames from ie/chrome/ff // methodName=$2, fileName=$4, lineNo=$5, column=$6 const stackFramesRegex = /^(\s+at)?(.*?)(\@|\s\(|\s)([^\(\n]+):(\d+):(\d+)(\)?)$/; +// regex to match native/built-in frames with no file location, e.g. " at Array.forEach ()" or " at Array.forEach ()" +const nativeFrameRegex = /^\s+at\s+(.+?)\s+\((|native)?\)$/; export class _StackFrame { public sizeInBytes = 0; @@ -111,6 +113,13 @@ export class _StackFrame { this.method = Util.getInstance().trim(matches[2]) || this.method; this.fileName = Util.getInstance().trim(matches[4]) || ""; this.line = parseInt(matches[5]) || 0; + } else { + const nativeMatches = frame.match(nativeFrameRegex); + if (nativeMatches) { + this.method = Util.getInstance().trim(nativeMatches[1]) || this.method; + this.fileName = ""; + this.line = 0; + } } this.sizeInBytes += this.method.length; @@ -134,7 +143,7 @@ export function parseStack(stack: any): _StackFrame[] { let totalSizeInBytes = 0; for (let i = 0; i <= frames.length; i++) { const frame = frames[i]; - if (stackFramesRegex.test(frame)) { + if (stackFramesRegex.test(frame) || nativeFrameRegex.test(frame)) { const parsedFrame = new _StackFrame(frames[i], level++); totalSizeInBytes += parsedFrame.sizeInBytes; parsedStack.push(parsedFrame); diff --git a/test/unitTests/logs/exceptions.tests.ts b/test/unitTests/logs/exceptions.tests.ts index 92c1b2fb0..fd3f2e31e 100644 --- a/test/unitTests/logs/exceptions.tests.ts +++ b/test/unitTests/logs/exceptions.tests.ts @@ -3,7 +3,7 @@ import assert from "assert"; import sinon from "sinon"; -import { AutoCollectExceptions } from "../../../src/logs/exceptions"; +import { AutoCollectExceptions, _StackFrame, parseStack } from "../../../src/logs/exceptions"; describe("AutoCollection/Exceptions", () => { let sandbox: sinon.SinonSandbox; @@ -41,3 +41,49 @@ describe("AutoCollection/Exceptions", () => { assert.equal(processRemoveListenerSpy.getCall(1).args[0], "unhandledRejection"); }); }); + +describe("StackFrame Parsing", () => { + it("should parse standard stack frames", () => { + const frame = new _StackFrame(" at Object. (/path/to/file.js:10:5)", 0); + assert.equal(frame.method, "Object."); + assert.equal(frame.fileName, "/path/to/file.js"); + assert.equal(frame.line, 10); + }); + + it("should parse native frames with empty parens (issue #698)", () => { + const frame = new _StackFrame(" at Array.forEach ()", 0); + assert.equal(frame.method, "Array.forEach"); + assert.equal(frame.fileName, ""); + assert.equal(frame.line, 0); + }); + + it("should parse native frames with ", () => { + const frame = new _StackFrame(" at Array.forEach ()", 0); + assert.equal(frame.method, "Array.forEach"); + assert.equal(frame.fileName, ""); + assert.equal(frame.line, 0); + }); + + it("should parse native frames with native keyword", () => { + const frame = new _StackFrame(" at Array.forEach (native)", 0); + assert.equal(frame.method, "Array.forEach"); + assert.equal(frame.fileName, ""); + assert.equal(frame.line, 0); + }); + + it("parseStack should include native frames in parsed output", () => { + const stack = [ + "Error: test", + " at Object. (/path/to/file.js:10:5)", + " at Array.forEach ()", + " at Module._compile (internal/modules/cjs/loader.js:778:30)", + ].join("\n"); + + const parsed = parseStack(stack); + assert.equal(parsed.length, 3); + assert.equal(parsed[0].method, "Object."); + assert.equal(parsed[1].method, "Array.forEach"); + assert.equal(parsed[1].fileName, ""); + assert.equal(parsed[2].method, "Module._compile"); + }); +});