From 444da5b161ad12ec92cca2b0cf7c6cf66c0f8831 Mon Sep 17 00:00:00 2001 From: Walt Jones Date: Fri, 1 Apr 2022 10:26:14 -0400 Subject: [PATCH] feat: support error cause --- src/browser/transforms.js | 4 +-- src/errorParser.js | 4 +-- src/server/transforms.js | 2 +- test/browser.transforms.test.js | 43 ++++++++++++++++++++++++++ test/server.transforms.test.js | 53 ++++++++++++++++++++++++++++++++- 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/src/browser/transforms.js b/src/browser/transforms.js index f88dfdc79..cab7b99c8 100644 --- a/src/browser/transforms.js +++ b/src/browser/transforms.js @@ -42,8 +42,8 @@ function addErrorContext(item) { chain.push(err); - while (err.nested) { - err = err.nested; + while (err.nested || err.cause) { + err = err.nested || err.cause; chain.push(err); } diff --git a/src/errorParser.js b/src/errorParser.js index 9a3389b7f..49824b04d 100644 --- a/src/errorParser.js +++ b/src/errorParser.js @@ -64,11 +64,11 @@ function Stack(exception, skip) { function parse(e, skip) { var err = e; - if (err.nested) { + if (err.nested || err.cause) { var traceChain = []; while (err) { traceChain.push(new Stack(err, skip)); - err = err.nested; + err = err.nested || err.cause; skip = 0; // Only apply skip value to primary error } diff --git a/src/server/transforms.js b/src/server/transforms.js index 418733c46..7eafecf09 100644 --- a/src/server/transforms.js +++ b/src/server/transforms.js @@ -85,7 +85,7 @@ function handleItemWithError(item, options, callback) { var chain = []; do { errors.push(err); - err = err.nested; + err = err.nested || err.cause; } while (err); item.stackInfo = chain; diff --git a/test/browser.transforms.test.js b/test/browser.transforms.test.js index 067361237..2e5f4761b 100644 --- a/test/browser.transforms.test.js +++ b/test/browser.transforms.test.js @@ -30,6 +30,10 @@ function itemFromArgs(args) { return item; } +function chromeMajorVersion() { + return parseInt(navigator.userAgent.match(/Chrome\/([0-9]+)\./)[1]); +} + describe('handleDomException', function() { it('should do nothing if not a DOMException', function(done) { var err = new Error('test'); @@ -452,6 +456,45 @@ describe('addBody', function() { }); }); }); + describe('with error cause', function() { + // Error cause was introduced in Chrome 93. + if (chromeMajorVersion() < 93) return; + + it('should create trace_chain', function(done) { + var causeErr = new Error('cause error'); + var err = new Error('test error', { cause: causeErr}); + var args = ['a message', err]; + var item = itemFromArgs(args); + var options = {}; + t.handleItemWithError(item, options, function(e, i) { + expect(i.stackInfo).to.be.ok(); + }); + t.addBody(item, options, function(e, i) { + expect(i.data.body.trace_chain.length).to.eql(2); + expect(i.data.body.trace_chain[0].exception.message).to.eql('test error'); + expect(i.data.body.trace_chain[1].exception.message).to.eql('cause error'); + done(e); + }); + }); + it('should create add error context as custom data', function(done) { + var causeErr = new Error('cause error'); + causeErr.rollbarContext = { err1: 'cause context' }; + var err = new Error('test error', { cause: causeErr}); + err.rollbarContext = { err2: 'error context' }; + var args = ['a message', err]; + var item = itemFromArgs(args); + var options = { addErrorContext: true }; + t.handleItemWithError(item, options, function(e, i) { + expect(i.stackInfo).to.be.ok(); + }); + t.addBody(item, options, function(e, i) { + expect(i.data.body.trace_chain.length).to.eql(2); + expect(i.data.custom.err1).to.eql('cause context'); + expect(i.data.custom.err2).to.eql('error context'); + done(e); + }); + }); + }); }); describe('scrubPayload', function() { diff --git a/test/server.transforms.test.js b/test/server.transforms.test.js index 4ee517f6e..8e12efa03 100644 --- a/test/server.transforms.test.js +++ b/test/server.transforms.test.js @@ -30,6 +30,22 @@ async function throwInScriptFile(rollbar, filepath, callback) { callback(rollbar); } +var nodeVersion = function () { + var version = process.versions.node.split('.'); + + return [ + parseInt(version[0]), + parseInt(version[1]), + parseInt(version[2]), + ]; +}(); + +var isMinNodeVersion = function(major, minor) { + return ( + nodeVersion[0] > major || (nodeVersion[0] === major && nodeVersion[1] >= minor) + ); +} + vows.describe('transforms') .addBatch({ 'baseData': { @@ -494,7 +510,42 @@ vows.describe('transforms') assert.equal(item.data.custom.err1, 'nested context'); assert.equal(item.data.custom.err2, 'error context'); } - } + }, + 'with an error cause': { + topic: function (options) { + var test = function() { + var x = thisVariableIsNotDefined; + }; + var err; + try { + test(); + } catch (e) { + err = new Error('cause message', { cause: e }); + e.rollbarContext = { err1: 'cause context' }; + err.rollbarContext = { err2: 'error context' }; + } + var item = { + data: {body: {}}, + err: err + }; + t.handleItemWithError(item, options, this.callback); + }, + 'should not error': function(err, item) { + assert.ifError(err); + }, + 'should have the right data in the trace_chain': function(err, item) { + // Error cause was introduced in Node 16.9. + if (!isMinNodeVersion(16, 9)) return; + + var trace_chain = item.stackInfo; + assert.lengthOf(trace_chain, 2); + assert.equal(trace_chain[0].exception.class, 'Error'); + assert.equal(trace_chain[0].exception.message, 'cause message'); + assert.equal(trace_chain[1].exception.class, 'ReferenceError'); + assert.equal(item.data.custom.err1, 'cause context'); + assert.equal(item.data.custom.err2, 'error context'); + } + }, } } }