Comparing version 1.2.1 to 2.0.0
@@ -0,1 +1,16 @@ | ||
# 2.0.0 - 5/10/2017 | ||
- Properly surface errors to preserve process exit conditions [See #308, #257] | ||
- Node processes with raven will now exit in exactly the same situations as if raven were not present | ||
- Previously, there were failure scenarios where raven would erroneously cause the process to continue to run when it should have shut down | ||
- **Be aware when upgrading:** it is possible that with raven-node 2.0, your node process will shut down from exceptions where it previously did not | ||
- This also includes changes to more reliably finish capturing the exception that induces process shutdown | ||
- Don't include `domain` property as extra property on `Error` objects [See #309] | ||
- Parse `req` object from context or kwargs [See #310] | ||
- For Express middleware users in particular, raven will now automatically report request details and user context in situations where it previously did not | ||
- Tidied up `.npmignore` to exclude unnecessary files in npm package [See #311] | ||
- Install size reduced from 484kB to 84kB, which should save npm ~100GB/month in bandwidth | ||
- Removed various deprecated methods [See #313] | ||
- Removed postgres autoBreadcrumbs to avoid webpack dependency issue [See #315, #254] | ||
- postgres (and more) autoBreadcrumbs will return in version 2.1 | ||
# 1.1.6, 1.2.1 - 4/7/2017 | ||
@@ -2,0 +17,0 @@ - Fix memory leak in `consoleAlert` (and thus, if not disabled, in `captureException`) [See #300] |
@@ -5,7 +5,3 @@ 'use strict'; | ||
module.exports.utils = require('./lib/utils'); | ||
module.exports.middleware = { | ||
connect: require('./lib/middleware/connect') | ||
}; | ||
// friendly alias for "raven.middleware.express" | ||
module.exports.middleware.express = module.exports.middleware.connect; | ||
module.exports.transports = require('./lib/transports'); | ||
@@ -12,0 +8,0 @@ module.exports.parsers = require('./lib/parsers'); |
@@ -116,16 +116,2 @@ 'use strict'; | ||
}, originals); | ||
}, | ||
pg: function (Raven) { | ||
// Using fill helper here is hard because of `this` binding | ||
var pg = require('pg'); | ||
var origQuery = pg.Connection.prototype.query; | ||
pg.Connection.prototype.query = function (text) { | ||
Raven.captureBreadcrumb({ | ||
category: 'postgres', | ||
message: text | ||
}); | ||
origQuery.call(this, text); | ||
}; | ||
originals.push([pg.Connection.prototype, 'query', origQuery]); | ||
} | ||
@@ -132,0 +118,0 @@ }; |
@@ -48,2 +48,3 @@ 'use strict'; | ||
this.transport = options.transport || transports[this.dsn.protocol]; | ||
this.sendTimeout = options.sendTimeout || 1; | ||
this.release = options.release || process.env.SENTRY_RELEASE || ''; | ||
@@ -59,3 +60,2 @@ this.environment = options.environment || process.env.SENTRY_ENVIRONMENT || ''; | ||
http: false, | ||
pg: false | ||
}; | ||
@@ -104,2 +104,8 @@ // default to 30, don't allow higher than 100 | ||
this.onFatalError = this.defaultOnFatalError = function (err, sendErr, eventId) { | ||
console.error(err && err.stack ? err.stack : err); | ||
process.exit(1); | ||
}; | ||
this.uncaughtErrorHandler = this.makeErrorHandler(); | ||
this.on('error', function (err) { | ||
@@ -112,12 +118,18 @@ utils.consoleAlert('failed to send exception to sentry: ' + err.message); | ||
install: function install(opts, cb) { | ||
install: function install(cb) { | ||
if (this.installed) return this; | ||
if (typeof opts === 'function') { | ||
cb = opts; | ||
if (typeof cb === 'function') { | ||
this.onFatalError = cb; | ||
} | ||
registerExceptionHandler(this, cb); | ||
process.on('uncaughtException', this.uncaughtErrorHandler); | ||
if (this.captureUnhandledRejections) { | ||
registerRejectionHandler(this, cb); | ||
var self = this; | ||
process.on('unhandledRejection', function (reason) { | ||
self.captureException(reason, function (sendErr, eventId) { | ||
if (!sendErr) utils.consoleAlert('unhandledRejection captured: ' + eventId); | ||
}); | ||
}); | ||
} | ||
@@ -150,2 +162,54 @@ | ||
makeErrorHandler: function () { | ||
var self = this; | ||
var caughtFirstError = false; | ||
var caughtSecondError = false; | ||
var calledFatalError = false; | ||
var firstError; | ||
return function (err) { | ||
if (!caughtFirstError) { | ||
// this is the first uncaught error and the ultimate reason for shutting down | ||
// we want to do absolutely everything possible to ensure it gets captured | ||
// also we want to make sure we don't go recursion crazy if more errors happen after this one | ||
firstError = err; | ||
caughtFirstError = true; | ||
self.captureException(err, function (sendErr, eventId) { | ||
if (!calledFatalError) { | ||
calledFatalError = true; | ||
self.onFatalError(err, sendErr, eventId); | ||
} | ||
}); | ||
} else if (calledFatalError) { | ||
// we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down | ||
utils.consoleAlert('uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown'); | ||
self.defaultOnFatalError(err); | ||
} else if (!caughtSecondError) { | ||
// two cases for how we can hit this branch: | ||
// - capturing of first error blew up and we just caught the exception from that | ||
// - quit trying to capture, proceed with shutdown | ||
// - a second independent error happened while waiting for first error to capture | ||
// - want to avoid causing premature shutdown before first error capture finishes | ||
// it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff | ||
// so let's instead just delay a bit before we proceed with our action here | ||
// in case 1, we just wait a bit unnecessarily but ultimately do the same thing | ||
// in case 2, the delay hopefully made us wait long enough for the capture to finish | ||
// two potential nonideal outcomes: | ||
// nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError | ||
// nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error | ||
// note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError) | ||
// we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish | ||
caughtSecondError = true; | ||
setTimeout(function () { | ||
if (!calledFatalError) { | ||
// it was probably case 1, let's treat err as the sendErr and call onFatalError | ||
calledFatalError = true; | ||
self.onFatalError(firstError, err); | ||
} else { | ||
// it was probably case 2, our first error finished capturing while we waited, cool, do nothing | ||
} | ||
}, (self.sendTimeout + 1) * 1000); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc | ||
} | ||
}; | ||
}, | ||
generateEventId: function generateEventId() { | ||
@@ -171,2 +235,17 @@ return uuid().replace(/-/g, ''); | ||
/* | ||
`request` is our specified property name for the http interface: https://docs.sentry.io/clientdev/interfaces/http/ | ||
`req` is the conventional name for a request object in node/express/etc | ||
we want to enable someone to pass a `request` property to kwargs according to http interface | ||
but also want to provide convenience for passing a req object and having us parse it out | ||
so we only parse a `req` property if the `request` property is absent/empty (and hence we won't clobber) | ||
parseUser returns a partial kwargs object with a `request` property and possibly a `user` property | ||
*/ | ||
kwargs.request = extend({}, this._globalContext.request, domainContext.request, kwargs.request); | ||
if (Object.keys(kwargs.request).length === 0) { | ||
var req = extend({}, this._globalContext.req, domainContext.req, kwargs.req); | ||
var parseUser = Object.keys(kwargs.user).length === 0 ? this.parseUser : false; | ||
kwargs = extend(kwargs, parsers.parseRequest(req, parseUser)); | ||
} | ||
kwargs.modules = utils.getModules(); | ||
@@ -267,8 +346,3 @@ kwargs.server_name = kwargs.server_name || this.name; | ||
/* The onErr param here is sort of ugly and won't typically be used | ||
* but it lets us write the requestHandler middleware in terms of this function. | ||
* We could consider getting rid of it and just duplicating the domain | ||
* instantiation etc logic in the requestHandler middleware | ||
*/ | ||
context: function (ctx, func, onErr) { | ||
context: function (ctx, func) { | ||
if (!func && typeof ctx === 'function') { | ||
@@ -282,6 +356,6 @@ func = ctx; | ||
// and i don't know if we need to support the args param; it's undocumented | ||
return this.wrap(ctx, func, onErr).apply(null); | ||
return this.wrap(ctx, func).apply(null); | ||
}, | ||
wrap: function (options, func, onErr) { | ||
wrap: function (options, func) { | ||
if (!func && typeof options === 'function') { | ||
@@ -296,10 +370,3 @@ func = options; | ||
var self = this; | ||
if (typeof onErr !== 'function') { | ||
onErr = function (err) { | ||
self.captureException(err); | ||
}; | ||
} | ||
wrapDomain.on('error', onErr); | ||
wrapDomain.on('error', this.uncaughtErrorHandler); | ||
var wrapped = wrapDomain.bind(func); | ||
@@ -412,7 +479,7 @@ | ||
return function (req, res, next) { | ||
self.context({}, function () { | ||
self.context({ req: req }, function () { | ||
domain.active.add(req); | ||
domain.active.add(res); | ||
next(); | ||
}, next); | ||
}); | ||
}; | ||
@@ -429,4 +496,3 @@ }, | ||
var kwargs = parsers.parseRequest(req, self.parseUser); | ||
var eventId = self.captureException(err, kwargs); | ||
var eventId = self.captureException(err, { req: req }); | ||
res.sentry = eventId; | ||
@@ -456,36 +522,2 @@ return next(err); | ||
// Deprecations | ||
extend(Raven.prototype, { | ||
getIdent: function getIdent(result) { | ||
utils.consoleAlertOnce('getIdent has been deprecated and will be removed in v2.0'); | ||
return result; | ||
}, | ||
captureError: function captureError() { | ||
utils.consoleAlertOnce('captureError has been deprecated and will be removed in v2.0; use captureException instead'); | ||
return this.captureException.apply(this, arguments); | ||
}, | ||
captureQuery: function captureQuery() { | ||
utils.consoleAlertOnce('captureQuery has been deprecated and will be removed in v2.0'); | ||
return this; | ||
}, | ||
patchGlobal: function (cb) { | ||
utils.consoleAlertOnce('patchGlobal has been deprecated and will be removed in v2.0; use install instead'); | ||
registerExceptionHandler(this, cb); | ||
return this; | ||
}, | ||
setUserContext: function setUserContext() { | ||
utils.consoleAlertOnce('setUserContext has been deprecated and will be removed in v2.0; use setContext instead'); | ||
return this; | ||
}, | ||
setExtraContext: function setExtraContext() { | ||
utils.consoleAlertOnce('setExtraContext has been deprecated and will be removed in v2.0; use setContext instead'); | ||
return this; | ||
}, | ||
setTagsContext: function setTagsContext() { | ||
utils.consoleAlertOnce('setTagsContext has been deprecated and will be removed in v2.0; use setContext instead'); | ||
return this; | ||
}, | ||
}); | ||
Raven.prototype.get_ident = Raven.prototype.getIdent; | ||
// Maintain old API compat, need to make sure arguments length is preserved | ||
@@ -503,3 +535,2 @@ function Client(dsn, options) { | ||
defaultInstance.Client = Client; | ||
defaultInstance.patchGlobal = patchGlobal; | ||
defaultInstance.version = require('../package.json').version; | ||
@@ -509,56 +540,1 @@ defaultInstance.disableConsoleAlerts = utils.disableConsoleAlerts; | ||
module.exports = defaultInstance; | ||
function registerExceptionHandler(client, cb) { | ||
var called = false; | ||
process.on('uncaughtException', function (err) { | ||
if (cb) { // bind event listeners only if a callback was supplied | ||
var onLogged = function onLogged() { | ||
called = false; | ||
cb(true, err); | ||
}; | ||
var onError = function onError() { | ||
called = false; | ||
cb(false, err); | ||
}; | ||
if (called) { | ||
client.removeListener('logged', onLogged); | ||
client.removeListener('error', onError); | ||
return cb(false, err); | ||
} | ||
client.once('logged', onLogged); | ||
client.once('error', onError); | ||
called = true; | ||
} | ||
var eventId = client.captureException(err); | ||
return utils.consoleAlert('uncaughtException: ' + eventId); | ||
}); | ||
} | ||
function registerRejectionHandler(client, cb) { | ||
process.on('unhandledRejection', function (reason) { | ||
var eventId = client.captureException(reason, function (sendErr) { | ||
cb && cb(!sendErr, reason); | ||
}); | ||
return utils.consoleAlert('unhandledRejection: ' + eventId); | ||
}); | ||
} | ||
function patchGlobal(client, cb) { | ||
// handle when the first argument is the callback, with no client specified | ||
if (typeof client === 'function') { | ||
cb = client; | ||
client = new Client(); | ||
// first argument is a string DSN | ||
} else if (typeof client === 'string') { | ||
client = new Client(client); | ||
} | ||
// at the end, if we still don't have a Client, let's make one! | ||
!(client instanceof Raven) && (client = new Client()); | ||
registerExceptionHandler(client, cb); | ||
} |
@@ -34,3 +34,3 @@ 'use strict'; | ||
if (err.hasOwnProperty(key)) { | ||
if (key !== 'name' && key !== 'message' && key !== 'stack') { | ||
if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== 'domain') { | ||
extraErrorProps = extraErrorProps || {}; | ||
@@ -37,0 +37,0 @@ extraErrorProps[key] = err[key]; |
@@ -5,8 +5,10 @@ 'use strict'; | ||
var util = require('util'); | ||
var timeoutReq = require('timed-out'); | ||
var http = require('http'); | ||
var https = require('https'); | ||
function Transport() {} | ||
util.inherits(Transport, events.EventEmitter); | ||
var http = require('http'); | ||
function HTTPTransport(options) { | ||
@@ -32,2 +34,3 @@ this.defaultPort = 80; | ||
} | ||
var req = this.transport.request(options, function (res) { | ||
@@ -56,2 +59,4 @@ res.setEncoding('utf8'); | ||
timeoutReq(req, client.sendTimeout * 1000); | ||
var cbFired = false; | ||
@@ -68,4 +73,2 @@ req.on('error', function (e) { | ||
var https = require('https'); | ||
function HTTPSTransport(options) { | ||
@@ -72,0 +75,0 @@ this.defaultPort = 443; |
@@ -12,3 +12,3 @@ { | ||
], | ||
"version": "1.2.1", | ||
"version": "2.0.0", | ||
"repository": "git://github.com/getsentry/raven-node.git", | ||
@@ -36,3 +36,4 @@ "author": "Matt Robenolt <matt@ydekproductions.com>", | ||
"uuid": "3.0.0", | ||
"stack-trace": "0.0.9" | ||
"stack-trace": "0.0.9", | ||
"timed-out": "4.0.1" | ||
}, | ||
@@ -39,0 +40,0 @@ "devDependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50135
6
11
973
+ Addedtimed-out@4.0.1
+ Addedtimed-out@4.0.1(transitive)