From b3bc106abc929190c47492b1dbb9ccd809bbcc88 Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" <terence@honles.com> Date: Thu, 14 Jan 2021 17:25:56 -0800 Subject: [PATCH] support logging "duck typed" Error objects In TypeScript Error objects might be implemented instead of extended (i.e. HttpErrorResponse [1]). This causes the `instanceof` check in createItem to fail to recognize the argument as an error, and this change fixes that. [1]: https://github.com/angular/angular/blob/9a5ac47331faf0f6030bff75ad8ef8dab2f8d2d9/packages/common/http/src/response.ts#L319 --- src/server/rollbar.js | 2 +- src/utility.js | 10 ++++++---- test/utility.test.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/server/rollbar.js b/src/server/rollbar.js index 55af8e1b2..947e8c385 100644 --- a/src/server/rollbar.js +++ b/src/server/rollbar.js @@ -280,7 +280,7 @@ Rollbar.prototype.errorHandler = function () { return next(err, request, response); } - if (err instanceof Error) { + if (_.isError(err)) { return this.error(err, request, cb); } return this.error('Error: ' + err, request, cb); diff --git a/src/utility.js b/src/utility.js index e64057cbb..0850bdd0f 100644 --- a/src/utility.js +++ b/src/utility.js @@ -139,8 +139,10 @@ function isIterable(i) { * @returns true if e is an error */ function isError(e) { - // Detect both Error and Firefox Exception type - return isType(e, 'error') || isType(e, 'exception'); + // Detect Error, Firefox Exception type and error like object + return (isType(e, 'error') + || isType(e, 'exception') + || (typeof e === 'object' && e && e.name && e.message)); } function redact() { @@ -410,7 +412,7 @@ function createItem(args, logger, notifier, requestKeys, lambdaContext) { break; case 'object': case 'array': - if (arg instanceof Error || (typeof DOMException !== 'undefined' && arg instanceof DOMException)) { + if (isError(arg)) { err ? extraArgs.push(arg) : err = arg; break; } @@ -428,7 +430,7 @@ function createItem(args, logger, notifier, requestKeys, lambdaContext) { custom ? extraArgs.push(arg) : custom = arg; break; default: - if (arg instanceof Error || (typeof DOMException !== 'undefined' && arg instanceof DOMException)) { + if (isError(arg)) { err ? extraArgs.push(arg) : err = arg; break; } diff --git a/test/utility.test.js b/test/utility.test.js index 2e7158efd..e839876e8 100644 --- a/test/utility.test.js +++ b/test/utility.test.js @@ -244,6 +244,38 @@ describe('isError', function() { expect(_.isError(e)).to.be.ok(); done(); }); + it('should handle classes implementing the error interface', function(done) { + // This is a mostly browser compliant way of doing this + // just for the sake of doing it, even though we mostly + // need this to work in node environments + function TestCustomError(message) { + Object.defineProperty(this, 'name', { + enumerable: false, + writable: false, + value: 'TestCustomError' + }); + + Object.defineProperty(this, 'message', { + enumerable: false, + writable: true, + value: message + }); + + if (Error.hasOwnProperty('captureStackTrace')) { + Error.captureStackTrace(this, TestCustomError); + } else { + Object.defineProperty(this, 'stack', { + enumerable: false, + writable: false, + value: (new Error(message)).stack + }); + } + } + + var e = new TestCustomError('bork'); + expect(_.isError(e)).to.be.ok(); + done(); + }); }); describe('merge', function() {