Skip to content

Commit

Permalink
Merge pull request #729 from rollbar/wj-capture-uncaught
Browse files Browse the repository at this point in the history
Allow enable/disable uncaught errors/rejections in .configure
  • Loading branch information
waltjones authored May 15, 2019
2 parents dcf595a + 86dec32 commit b5f9515
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 16 deletions.
23 changes: 23 additions & 0 deletions sdks/rollbar.js/examples/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Generate errors for test automation</title>
<!-- Throwing inside karma test files won't work,
but you can do it here and let karma load this file. -->
<script>
window.throwError = function throwError() {
// Example error, which will be reported to rollbar when `captureUncaught`
// is true in the config.
throw new Error('test error');
};
</script>
</head>
<body>
<div style="text-align:center">
<h1>
Generate errors for test automation
</h1>
</div>
<button id="throw-error" onclick="throwError()">Throw Error</button>
</html>
42 changes: 33 additions & 9 deletions sdks/rollbar.js/src/browser/rollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,11 @@ function Rollbar(options, client) {
var api = new API(this.options, transport, urllib);
this.client = client || new Client(this.options, api, logger, 'browser');

var gWindow = ((typeof window != 'undefined') && window) || ((typeof self != 'undefined') && self);
var gWindow = _gWindow();
var gDocument = (typeof document != 'undefined') && document;
addTransformsToNotifier(this.client.notifier, gWindow);
addPredicatesToQueue(this.client.queue);
if (this.options.captureUncaught || this.options.handleUncaughtExceptions) {
globals.captureUncaughtExceptions(gWindow, this);
globals.wrapGlobals(gWindow, this);
}
if (this.options.captureUnhandledRejections || this.options.handleUnhandledRejections) {
globals.captureUnhandledRejections(gWindow, this);
}

this.setupUnhandledCapture();
this.instrumenter = new Instrumenter(this.options, this.client.telemeter, this, gWindow, gDocument);
this.instrumenter.instrument();
}
Expand Down Expand Up @@ -73,6 +66,7 @@ Rollbar.prototype.configure = function(options, payloadData) {
this.options = _.handleOptions(oldOptions, options, payload);
this.client.configure(this.options, payloadData);
this.instrumenter.configure(this.options);
this.setupUnhandledCapture();
return this;
};
Rollbar.configure = function(options, payloadData) {
Expand Down Expand Up @@ -221,7 +215,29 @@ Rollbar.sendJsonPayload = function() {
}
};

Rollbar.prototype.setupUnhandledCapture = function() {
var gWindow = _gWindow();

if (!this.unhandledExceptionsInitialized) {
if (this.options.captureUncaught || this.options.handleUncaughtExceptions) {
globals.captureUncaughtExceptions(gWindow, this);
globals.wrapGlobals(gWindow, this);
this.unhandledExceptionsInitialized = true;
}
}
if (!this.unhandledRejectionsInitialized) {
if (this.options.captureUnhandledRejections || this.options.handleUnhandledRejections) {
globals.captureUnhandledRejections(gWindow, this);
this.unhandledRejectionsInitialized = true;
}
}
};

Rollbar.prototype.handleUncaughtException = function(message, url, lineno, colno, error, context) {
if (!this.options.captureUncaught && !this.options.handleUncaughtExceptions) {
return;
}

var item;
var stackInfo = _.makeUnhandledStackInfo(
message,
Expand Down Expand Up @@ -249,6 +265,10 @@ Rollbar.prototype.handleUncaughtException = function(message, url, lineno, colno
};

Rollbar.prototype.handleUnhandledRejection = function(reason, promise) {
if (!this.options.captureUnhandledRejections && !this.options.handleUnhandledRejections) {
return;
}

var message = 'unhandled rejection was null or undefined!';
if (reason) {
if (reason.message) {
Expand Down Expand Up @@ -418,6 +438,10 @@ function _getFirstFunction(args) {
return undefined;
}

function _gWindow() {
return ((typeof window != 'undefined') && window) || ((typeof self != 'undefined') && self);
}

/* global __NOTIFIER_VERSION__:false */
/* global __DEFAULT_BROWSER_SCRUB_FIELDS__:false */
/* global __DEFAULT_LOG_LEVEL__:false */
Expand Down
26 changes: 19 additions & 7 deletions sdks/rollbar.js/src/server/rollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,7 @@ function Rollbar(options, client) {
this.client = client || new Client(this.options, api, logger, 'server');
addTransformsToNotifier(this.client.notifier);
addPredicatesToQueue(this.client.queue);

if (this.options.captureUncaught || this.options.handleUncaughtExceptions) {
this.handleUncaughtExceptions();
}
if (this.options.captureUnhandledRejections || this.options.handleUnhandledRejections) {
this.handleUnhandledRejections();
}
this.setupUnhandledCapture();
}

var _instance = null;
Expand Down Expand Up @@ -89,6 +83,7 @@ Rollbar.prototype.configure = function(options, payloadData) {
delete this.options.maxItems;
logger.setVerbose(this.options.verbose);
this.client.configure(options, payloadData);
this.setupUnhandledCapture();
return this;
};
Rollbar.configure = function(options, payloadData) {
Expand Down Expand Up @@ -524,11 +519,24 @@ function _getFirstFunction(args) {
return undefined;
}

Rollbar.prototype.setupUnhandledCapture = function() {
if (this.options.captureUncaught || this.options.handleUncaughtExceptions) {
this.handleUncaughtExceptions();
}
if (this.options.captureUnhandledRejections || this.options.handleUnhandledRejections) {
this.handleUnhandledRejections();
}
};

Rollbar.prototype.handleUncaughtExceptions = function() {
var exitOnUncaught = !!this.options.exitOnUncaughtException;
delete this.options.exitOnUncaughtException;

addOrReplaceRollbarHandler('uncaughtException', function(err) {
if (!this.options.captureUncaught && !this.options.handleUncaughtExceptions) {
return;
}

this._uncaughtError(err, function(err) {
if (err) {
logger.error('Encountered error while handling an uncaught exception.');
Expand All @@ -547,6 +555,10 @@ Rollbar.prototype.handleUncaughtExceptions = function() {

Rollbar.prototype.handleUnhandledRejections = function() {
addOrReplaceRollbarHandler('unhandledRejection', function(reason) {
if (!this.options.captureUnhandledRejections && !this.options.handleUnhandledRejections) {
return;
}

this._uncaughtError(reason, function(err) {
if (err) {
logger.error('Encountered error while handling an uncaught exception.');
Expand Down
212 changes: 212 additions & 0 deletions sdks/rollbar.js/test/browser.rollbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,218 @@ describe('configure', function() {
});
});

describe('options.captureUncaught', function() {
before(function (done) {
// Load the HTML page, so errors can be generated.
document.write(window.__html__['examples/error.html']);

window.server = sinon.createFakeServer();
done();
});

after(function () {
window.server.restore();
});

function stubResponse(server) {
server.respondWith('POST', 'api/1/item',
[
200,
{ 'Content-Type': 'application/json' },
'{"err": 0, "result":{ "uuid": "d4c7acef55bf4c9ea95e4fe9428a8287"}}'
]
);
}

it('should capture when enabled in constructor', function(done) {
var server = window.server;
stubResponse(server);
server.requests.length = 0;

var options = {
accessToken: 'POST_CLIENT_ITEM_TOKEN',
captureUncaught: true
};
var rollbar = new Rollbar(options);

var element = document.getElementById('throw-error');
element.click();
server.respond();

var body = JSON.parse(server.requests[0].requestBody);

expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
expect(body.data.body.trace.exception.message).to.eql('test error');

// karma doesn't unload the browser between tests, so the onerror handler
// will remain installed. Unset captureUncaught so the onerror handler
// won't affect other tests.
rollbar.configure({
captureUncaught: false
});

done();
});

it('should respond to enable/disable in configure', function(done) {
var server = window.server;
var element = document.getElementById('throw-error');
stubResponse(server);
server.requests.length = 0;

var options = {
accessToken: 'POST_CLIENT_ITEM_TOKEN',
captureUncaught: false
};
var rollbar = new Rollbar(options);

element.click();
server.respond();
expect(server.requests.length).to.eql(0); // Disabled, no event
server.requests.length = 0;

rollbar.configure({
captureUncaught: true
});

element.click();
server.respond();

var body = JSON.parse(server.requests[0].requestBody);

expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
expect(body.data.body.trace.exception.message).to.eql('test error');

server.requests.length = 0;

rollbar.configure({
captureUncaught: false
});

element.click();
server.respond();
expect(server.requests.length).to.eql(0); // Disabled, no event

done();
});
});

describe('options.captureUnhandledRejections', function() {
before(function (done) {
window.server = sinon.createFakeServer();
done();
});

after(function () {
window.server.restore();
});

function stubResponse(server) {
server.respondWith('POST', 'api/1/item',
[
200,
{ 'Content-Type': 'application/json' },
'{"err": 0, "result":{ "uuid": "d4c7acef55bf4c9ea95e4fe9428a8287"}}'
]
);
}

it('should capture when enabled in constructor', function(done) {
var server = window.server;
stubResponse(server);
server.requests.length = 0;

var options = {
accessToken: 'POST_CLIENT_ITEM_TOKEN',
captureUnhandledRejections: true
};
var rollbar = new Rollbar(options);

Promise.reject(new Error('test reject'));

setTimeout(function() {
server.respond();

var body = JSON.parse(server.requests[0].requestBody);

expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
expect(body.data.body.trace.exception.message).to.eql('test reject');

rollbar.configure({
captureUnhandledRejections: false
});
window.removeEventListener('unhandledrejection', window._rollbarURH);

done();
}, 500);
});

it('should respond to enable in configure', function(done) {
var server = window.server;
stubResponse(server);
server.requests.length = 0;

var options = {
accessToken: 'POST_CLIENT_ITEM_TOKEN',
captureUnhandledRejections: false
};
var rollbar = new Rollbar(options);

rollbar.configure({
captureUnhandledRejections: true
});

Promise.reject(new Error('test reject'));

setTimeout(function() {
server.respond();

var body = JSON.parse(server.requests[0].requestBody);

expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
expect(body.data.body.trace.exception.message).to.eql('test reject');

server.requests.length = 0;

rollbar.configure({
captureUnhandledRejections: false
});
window.removeEventListener('unhandledrejection', window._rollbarURH);

done();
}, 500);
});

it('should respond to disable in configure', function(done) {
var server = window.server;
stubResponse(server);
server.requests.length = 0;

var options = {
accessToken: 'POST_CLIENT_ITEM_TOKEN',
captureUnhandledRejections: true
};
var rollbar = new Rollbar(options);

rollbar.configure({
captureUnhandledRejections: false
});

Promise.reject(new Error('test reject'));

setTimeout(function() {
server.respond();

expect(server.requests.length).to.eql(0); // Disabled, no event
server.requests.length = 0;

window.removeEventListener('unhandledrejection', window._rollbarURH);

done();
}, 500);
})
});

describe('captureEvent', function() {
it('should handle missing/default type and level', function(done) {
var options = {};
Expand Down
Loading

0 comments on commit b5f9515

Please sign in to comment.