Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

WIP: Add support for sending event requests via proxy #370

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ed0172a
Add support for sending event requests via proxy
ndmanvar Sep 11, 2017
b0bb3cb
add maxReqQueueCount in case sentry server is down
FennZheng Nov 5, 2015
d5a538e
Add test for maxReqQueueCount
kamilogorek Sep 8, 2017
beb40bf
feat: Make private key in DSN optional
kamilogorek Sep 18, 2017
28b7624
docs: Add Loopback integration docs
kamilogorek Sep 19, 2017
d1da947
fix: Pull Error's name from constructor, not Error itself
kamilogorek Sep 13, 2017
abdfb71
Add test for maxReqQueueCount
kamilogorek Sep 8, 2017
4f89ae2
docs: Fix syntax error for Loopback integration
kamilogorek Sep 20, 2017
3849f33
ref: Use local json-stringify-save package
kamilogorek Sep 21, 2017
2e971be
feat: Add Errors serialization to json-stringify-safe
kamilogorek Sep 21, 2017
9eebf71
docs: Mention git-rev usage in release config attribute
kamilogorek Sep 25, 2017
e5e5b22
docs: Add Sails.js integration docs
kamilogorek Sep 25, 2017
6c146b2
ci: Trigger build only once and cache deps
kamilogorek Sep 25, 2017
01e6de7
docs: Add name config description and optional ENV vars
kamilogorek Sep 22, 2017
b13ca83
feat(transport): enable keep-alives and limit number of sockets
mattrobenolt Mar 5, 2017
f5d3c08
feat(transport): retrieve socket name from http/s agents and add tests
kamilogorek Sep 12, 2017
41c10c4
feat: Fall back to NODE_ENV for Sentry Environment
kamilogorek Sep 26, 2017
ae75498
feat: Preserve some non-enumerable properties from request
kamilogorek Sep 22, 2017
f2801db
Rewrote tests and simplified implementation
kamilogorek Sep 26, 2017
588ac67
docs: Include information about fatalError install callback
kamilogorek Sep 22, 2017
443ea3a
2.2.0
kamilogorek Oct 2, 2017
151b6b5
Remove memwatch-next dependency
kamilogorek Oct 2, 2017
6d24403
fix: attach remaining non-enumerables to req
kamilogorek Oct 5, 2017
3f3d719
feat: Allow to configure stacktrace for captureMessage calls
kamilogorek Oct 5, 2017
ebcb270
docs: Add more clarification to req/request object
kamilogorek Oct 6, 2017
3b1c6e7
Fix typo in docs/config.rst
jorrit Oct 14, 2017
bf9dbab
Use tunnel-agent for proxying https request
ndmanvar Nov 17, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
branches:
only:
- master
sudo: false
language: node_js
node_js:
Expand All @@ -6,4 +9,7 @@ node_js:
- "6"
- "7"
- "8"
cache:
directories:
- node_modules
script: npm run test-full
14 changes: 14 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# 2.2.1 - 10/02/2017

- Remove unintentional memwatch-next dependency

# 2.2.0 - 10/02/2017
- Fixed all Node v8 tests to ensure everything is working correctly [See #366]
- Raven now uses Prettier to format it's code [See #364]
- Prevent Raven from queueing too many requests in case server is down [See #132]
- Enable keep-alive on socket connection and limit number of sockets [See #284]
- Pull Error's name from constructor, not Error itself to always get correct error type [See #372]
- Updated Errors serialization to store all additional properties and allow for attaching other object instances directly to it [See #376]
- Preserve some non-enumerable properties from request [See #379]
- Fall back to NODE_ENV for Sentry Environment [See #384]

# 2.1.2 - 8/16/2017
- Remove errant large file that accidentally got published in 2.1.1. [See #361]

Expand Down
54 changes: 48 additions & 6 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@ To get started, you need to configure Raven to use your Sentry DSN:
.. sourcecode:: javascript

var Raven = require('raven');
Raven.config('___PUBLIC_DSN___').install()
Raven.config('___PUBLIC_DSN___').install();

At this point, Raven is ready to capture any uncaught exceptions.

Note that the ``install`` method can optionally take a callback function that is invoked if a fatal, non-recoverable error occurs.
You can use this callback to perform any cleanup that should occur before the Node process exits.

.. sourcecode:: javascript

Raven.config('___PUBLIC_DSN___').install(function (err, initialErr, eventId) {
console.error(err);
process.exit(1);
});

Optional settings
-----------------

Expand All @@ -18,8 +28,8 @@ Optional settings
.. sourcecode:: javascript

Raven.config('___PUBLIC_DSN___', {
release: '1.3.0'
}).install()
release: '1.3.0'
}).install();

Those configuration options are documented below:

Expand All @@ -33,19 +43,44 @@ Those configuration options are documented below:
logger: 'default'
}

.. describe:: name

Set the server name for the client to use. Default: ``require('os').hostname()``
Optionally, use ``SENTRY_NAME`` environment variable.

.. code-block:: javascript

{
name: 'primary'
}

.. describe:: release

Track the version of your application in Sentry.
Optionally, use ``SENTRY_RELEASE`` environment variable.

.. code-block:: javascript

{
release: '721e41770371db95eee98ca2707686226b993eda'
}

This is usually a Git SHA hash, which can be obtained using various npm packages, e.g.

.. code-block:: javascript

var git = require('git-rev-sync');

{
// this will return 40 characters long hash
// eg. '75bf4eea9aa1a7fd6505d0d0aa43105feafa92ef'
release: git.long()
}

.. describe:: environment

Track the environment name inside Sentry.
Optionally, use ``SENTRY_ENVIRONMENT`` environment variable.

.. code-block:: javascript

Expand Down Expand Up @@ -195,6 +230,14 @@ Those configuration options are documented below:
Please see the raven-node source code to see `how transports are implemented
<https://github.com/getsentry/raven-node/blob/master/lib/transports.js>`__.

.. describe:: maxReqQueueCount

Controls how many requests can be maximally queued before bailing out and emitting an error. Defaults to `100`.

.. describe:: stacktrace

Attach stack trace to `captureMessage` calls by generatic "synthetic" error object and extracting all frames.

Environment Variables
---------------------

Expand All @@ -204,13 +247,12 @@ Environment Variables

.. describe:: SENTRY_NAME

Optionally set the name for the client to use. `What is name?
<http://raven.readthedocs.org/en/latest/config/index.html#name>`__
Optionally set the server name for the client to use.

.. describe:: SENTRY_RELEASE

Optionally set the application release version for the client to use, this is usually a Git SHA hash.

.. describe:: SENTRY_ENVIRONMENT

Optionally set the environment name, e.g. "staging", "production".
Optionally set the environment name, e.g. "staging", "production". Sentry will default to the value of `NODE_ENV`, if present.
2 changes: 2 additions & 0 deletions docs/integrations/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ Integrations
connect
express
koa
loopback
sails
21 changes: 21 additions & 0 deletions docs/integrations/loopback.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Loopback
========

.. code-block:: javascript

// server/middleware/sentry.js

var Raven = require('raven');
Raven.config('__DSN__').install();

module.exports = function () {
return Raven.errorHandler();
}

.. code-block:: javascript

// server/middleware.json

"final": {
"./middleware/sentry": {}
}
30 changes: 30 additions & 0 deletions docs/integrations/sails.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Sails
=====

.. code-block:: javascript

// config/http.js

var Raven = require('raven');
Raven.config('__DSN__').install();

module.exports.http = {
middleware: {
// Raven's handlers has to be added as a keys to http.middleware config object
requestHandler: Raven.requestHandler(),
errorHandler: Raven.errorHandler(),

// And they have to be added in a correct order to middlewares list
order: [
// The request handler must be the very first one
'requestHandler',
// ...more middlewares
'router',
// The error handler must be after router, but before any other error middleware
'errorHandler',
/// ...remaining middlewares
]
}
}


9 changes: 5 additions & 4 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,11 @@ All optional attributes are passed as part of the options to ``captureException`

If you're inside a context and your context data includes a ``user`` key, that data will be merged into this.

.. describe:: req
.. describe:: request

The ``req`` object associated with this event, from a Node http server, Express, Koa, or similar.
Will be parsed for request details and user context from ``req.user`` if present.
Alias: ``req``. The ``request`` object associated with this event, from a Node http server, Express, Koa, or similar.
Will be parsed for request details and user context from ``request.user`` if present. It will only pull out the data
that's handled by the server: ``headers``, ``method``, ``host``, ``protocol``, ``url``, ``query``, ``cookies``, ``body``, ``ip`` and ``user``.

.. code-block:: javascript

Expand Down Expand Up @@ -299,7 +300,7 @@ It can do anything necessary, including asynchronous operations, to make a best
not throw, and it absolutely must not allow the process to keep running indefinitely. This means it should probably make an explicit ``process.exit()`` call.

After catching a fatal exception, Raven will make a best-effort attempt to send it to Sentry before it calls the fatal exception handler.
If sending fails, a ``sendErr`` error object will be passed, and otherwise the ``eventId`` will be provided. In either case, the error object
If sending fails, a ``sendErr`` error object will be passed, and otherwise the ``eventId`` will be provided. In either case, the error object
resulting in the shutdown is passed as the first parameter.

.. code-block:: javascript
Expand Down
84 changes: 77 additions & 7 deletions lib/client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var stringify = require('json-stringify-safe');
var stringify = require('../vendor/json-stringify-safe');
var parsers = require('./parsers');
var zlib = require('zlib');
var utils = require('./utils');
Expand Down Expand Up @@ -54,7 +54,8 @@ extend(Raven.prototype, {
this.transport = options.transport || transports[this.dsn.protocol];
this.sendTimeout = options.sendTimeout || 1;
this.release = options.release || process.env.SENTRY_RELEASE || '';
this.environment = options.environment || process.env.SENTRY_ENVIRONMENT || '';
this.environment =
options.environment || process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || '';

// autoBreadcrumbs: true enables all, autoBreadcrumbs: false disables all
// autoBreadcrumbs: { http: true } enables a single type
Expand All @@ -67,7 +68,9 @@ extend(Raven.prototype, {
this.dataCallback = options.dataCallback;
this.shouldSendCallback = options.shouldSendCallback;
this.sampleRate = typeof options.sampleRate === 'undefined' ? 1 : options.sampleRate;
this.maxReqQueueCount = options.maxReqQueueCount || 100;
this.parseUser = options.parseUser;
this.stacktrace = options.stacktrace || false;

if (!this.dsn) {
utils.consoleAlert('no DSN provided, error reporting disabled');
Expand Down Expand Up @@ -227,15 +230,18 @@ extend(Raven.prototype, {
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(
{},
*/
kwargs.request = this._createRequestObject(
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 req = this._createRequestObject(
this._globalContext.req,
domainContext.req,
kwargs.req
);
if (Object.keys(req).length > 0) {
var parseUser = Object.keys(kwargs.user).length === 0 ? this.parseUser : false;
extend(kwargs, parsers.parseRequest(req, parseUser));
Expand Down Expand Up @@ -313,8 +319,31 @@ extend(Raven.prototype, {
} else {
kwargs = kwargs || {};
}

var eventId = this.generateEventId();
this.process(eventId, parsers.parseText(message, kwargs), cb);

if (this.stacktrace) {
var ex;
// Generate a "synthetic" stack trace
try {
throw new Error(message);
} catch (ex1) {
ex = ex1;
}

utils.parseStack(
ex,
function(frames) {
// We trim last frame, as it's our `throw new Error(message)` call itself, which is redundant
kwargs.stacktrace = {
frames: frames.slice(0, -1)
};
this.process(eventId, parsers.parseText(message, kwargs), cb);
}.bind(this)
);
} else {
this.process(eventId, parsers.parseText(message, kwargs), cb);
}

return eventId;
},
Expand Down Expand Up @@ -512,6 +541,47 @@ extend(Raven.prototype, {
currCtx.breadcrumbs.shift();
}
this.setContext(currCtx);
},

_createRequestObject: function() {
/**
* When using proxy, some of the attributes of req/request objects are non-enumerable.
* To make sure, that they are still available to us after we consolidate our sources
* (eg. globalContext.request + domainContext.request + kwargs.request),
* we manually pull them out from original objects.
*
* Same scenario happens when some frameworks (eg. Koa) decide to use request within
* request. eg `this.request.req`, which adds aliases to the main `request` object.
* By manually reassigning them here, we don't need to add additional checks
* like `req.method || (req.req && req.req.method)`
*
* We don't use Object.assign/extend as it's only merging over objects own properties,
* and we don't want to go through all of the properties as well, as we simply don't
* need all of them.
**/
var sources = Array.from(arguments).filter(function(source) {
return Object.prototype.toString.call(source) === '[object Object]';
});
sources = [{}].concat(sources);
var request = extend.apply(null, sources);
var nonEnumberables = [
'headers',
'host',
'ip',
'method',
'protocol',
'query',
'secure',
'url'
];

nonEnumberables.forEach(function(key) {
sources.forEach(function(source) {
if (source[key]) request[key] = source[key];
});
});

return request;
}
});

Expand Down
5 changes: 3 additions & 2 deletions lib/parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var cookie = require('cookie');
var urlParser = require('url');
var stringify = require('json-stringify-safe');
var stringify = require('../vendor/json-stringify-safe');

var utils = require('./utils');

Expand All @@ -15,7 +15,8 @@ module.exports.parseText = function parseText(message, kwargs) {

module.exports.parseError = function parseError(err, kwargs, cb) {
utils.parseStack(err, function(frames) {
var name = err.name + '';
var name =
({}.hasOwnProperty.call(err, 'name') ? err.name : err.constructor.name) + '';
if (typeof kwargs.message === 'undefined') {
kwargs.message = name + ': ' + (err.message || '<no message>');
}
Expand Down
Loading