Log errors to Sentry (#1422)
This commit is contained in:
committed by
Thaddée Tyl
parent
ea4b758612
commit
fe4ac0bf1c
@@ -131,10 +131,12 @@ SVG or JSON output. When deliberately changing the output, run
|
||||
`SNAPSHOT_DRY=1 npm run test:js:server` to preview changes to the saved
|
||||
snapshots, and `SNAPSHOT_UPDATE=1 npm run test:js:server` to update them.
|
||||
|
||||
The server can be [configured][sentry configuration] to use [Sentry][sentry].
|
||||
|
||||
[package manager]: https://nodejs.org/en/download/package-manager/
|
||||
[snapshot tests]: https://glebbahmutov.com/blog/snapshot-testing/
|
||||
|
||||
[sentry configuration]: doc/self-hosting.md#sentry
|
||||
[Sentry]: https://sentry.io/
|
||||
|
||||
Hosting your own server
|
||||
-----------------------
|
||||
|
||||
@@ -176,3 +176,26 @@ Set the `REDIRECT_URI` environment variable:
|
||||
```sh
|
||||
REDIRECT_URI=http://my-custom-shields.s3.amazonaws.com/
|
||||
```
|
||||
|
||||
Sentry
|
||||
------
|
||||
|
||||
In order to enable integration with [Sentry](https://sentry.io), you need your own [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn). It’s an URL in format `https://{PUBLIC_KEY}:{SECRET_KEY}@sentry.io/{PROJECT_ID}`.
|
||||
|
||||
### How to obtain the Sentry DSN
|
||||
|
||||
1. [Sign up](https://sentry.io/pricing/) for Sentry
|
||||
1. Log in to Sentry
|
||||
1. Create a new project for Node.js
|
||||
1. You should see [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn) for your project. Sentry DSN can be found by navigating to \[Project Name] -> Project Settings -> Client Keys (DSN) as well.
|
||||
|
||||
Start the server using the Sentry DSN. You can set it:
|
||||
- by `SENTRY_DSN` environment variable
|
||||
```
|
||||
SENTRY_DSN=https://xxx:yyy@sentry.io/zzz sudo node server
|
||||
```
|
||||
|
||||
- or by `sentry_dsn` secret property defined in `private/secret.json`
|
||||
```
|
||||
sudo node server
|
||||
```
|
||||
|
||||
11
lib/log.js
11
lib/log.js
@@ -1,4 +1,6 @@
|
||||
'use strict';
|
||||
const Raven = require('raven');
|
||||
const throttle = require('lodash.throttle');
|
||||
|
||||
const listeners = [];
|
||||
|
||||
@@ -26,9 +28,16 @@ module.exports = function log(...msg) {
|
||||
console.log(d, ...msg);
|
||||
};
|
||||
|
||||
const throttledConsoleError = throttle(console.error, 10000, { trailing: false });
|
||||
|
||||
module.exports.error = function error(...msg) {
|
||||
const d = date();
|
||||
listeners.forEach(f => f(d, ...msg))
|
||||
listeners.forEach(f => f(d, ...msg));
|
||||
Raven.captureException(msg, function (sendErr) {
|
||||
if (sendErr) {
|
||||
throttledConsoleError('Failed to send captured exception to Sentry', sendErr.message);
|
||||
}
|
||||
});
|
||||
console.error(d, ...msg);
|
||||
};
|
||||
|
||||
|
||||
48
lib/log.spec.js
Normal file
48
lib/log.spec.js
Normal file
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
const chai = require('chai');
|
||||
const expect = chai.expect;
|
||||
const sinon = require('sinon');
|
||||
const sinonChai = require('sinon-chai');;
|
||||
const Raven = require('raven');
|
||||
|
||||
chai.use(sinonChai);
|
||||
|
||||
function requireUncached(module){
|
||||
delete require.cache[require.resolve(module)]
|
||||
return require(module)
|
||||
}
|
||||
|
||||
describe('log', () => {
|
||||
describe('error', () => {
|
||||
before(() => {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
sinon.stub(Raven, 'captureException').callsFake(function fakeFn(e, callback) {
|
||||
callback(new Error(`Cannot send message "${e}" to Sentry.`));
|
||||
});
|
||||
// we have to create a spy before requiring 'error' function
|
||||
sinon.spy(console, 'error');
|
||||
this.error = requireUncached('./log').error;
|
||||
});
|
||||
|
||||
after(() => {
|
||||
this.clock.restore();
|
||||
console.error.restore();
|
||||
Raven.captureException.restore();
|
||||
});
|
||||
|
||||
it('should throttle errors from Raven client', () => {
|
||||
this.error('test error 1');
|
||||
this.error('test error 2');
|
||||
this.error('test error 3');
|
||||
this.clock.tick(11000);
|
||||
this.error('test error 4');
|
||||
this.error('test error 5');
|
||||
|
||||
expect(console.error).to.be.calledWithExactly('Failed to send captured exception to Sentry', 'Cannot send message "test error 1" to Sentry.');
|
||||
expect(console.error).to.not.be.calledWithExactly('Failed to send captured exception to Sentry', 'Cannot send message "test error 2" to Sentry.');
|
||||
expect(console.error).to.not.be.calledWithExactly('Failed to send captured exception to Sentry', 'Cannot send message "test error 3" to Sentry.');
|
||||
expect(console.error).to.be.calledWithExactly('Failed to send captured exception to Sentry', 'Cannot send message "test error 4" to Sentry.');
|
||||
expect(console.error).to.not.be.calledWithExactly('Failed to send captured exception to Sentry', 'Cannot send message "test error 5" to Sentry.');
|
||||
});
|
||||
});
|
||||
});
|
||||
55
package-lock.json
generated
55
package-lock.json
generated
@@ -2429,6 +2429,11 @@
|
||||
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
|
||||
"dev": true
|
||||
},
|
||||
"charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
|
||||
},
|
||||
"check-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||
@@ -3245,6 +3250,11 @@
|
||||
"which": "1.3.0"
|
||||
}
|
||||
},
|
||||
"crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
|
||||
@@ -6908,8 +6918,7 @@
|
||||
"is-buffer": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
|
||||
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
|
||||
},
|
||||
"is-builtin-module": {
|
||||
"version": "1.0.0",
|
||||
@@ -8021,6 +8030,11 @@
|
||||
"integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
@@ -8134,6 +8148,16 @@
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||
"integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
|
||||
"requires": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "1.1.5"
|
||||
}
|
||||
},
|
||||
"md5-file": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz",
|
||||
@@ -10268,6 +10292,25 @@
|
||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
|
||||
"dev": true
|
||||
},
|
||||
"raven": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/raven/-/raven-2.4.2.tgz",
|
||||
"integrity": "sha1-ASnircMHiGRv1TC2fQioziXU9tw=",
|
||||
"requires": {
|
||||
"cookie": "0.3.1",
|
||||
"md5": "2.2.1",
|
||||
"stack-trace": "0.0.9",
|
||||
"timed-out": "4.0.1",
|
||||
"uuid": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz",
|
||||
"integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg="
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.5.tgz",
|
||||
@@ -11575,6 +11618,11 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"stack-trace": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
|
||||
"integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU="
|
||||
},
|
||||
"stackframe": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz",
|
||||
@@ -12338,8 +12386,7 @@
|
||||
"timed-out": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
|
||||
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
|
||||
},
|
||||
"timers-browserify": {
|
||||
"version": "2.0.6",
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
"jsonpath": "~1.0.0",
|
||||
"lodash.countby": "^4.6.0",
|
||||
"lodash.mapkeys": "^4.6.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"lodash.uniq": "~4.5.0",
|
||||
"moment": "^2.19.3",
|
||||
"node-env-flag": "^0.1.0",
|
||||
"pdfkit": "~0.8.0",
|
||||
"pretty-bytes": "^4.0.2",
|
||||
"query-string": "^6.0.0",
|
||||
"raven": "^2.4.2",
|
||||
"redis": "~2.6.2",
|
||||
"request": "~2.85.0",
|
||||
"semver": "~5.5.0",
|
||||
|
||||
@@ -10,6 +10,11 @@ const queryString = require('query-string');
|
||||
const semver = require('semver');
|
||||
const xml2js = require('xml2js');
|
||||
const xpath = require('xpath');
|
||||
const Raven = require('raven');
|
||||
|
||||
const serverSecrets = require('./lib/server-secrets');
|
||||
Raven.config(process.env.SENTRY_DSN || serverSecrets.sentry_dsn).install();
|
||||
Raven.disableConsoleAlerts();
|
||||
|
||||
const { isDeprecated, getDeprecatedBadge } = require('./lib/deprecation-helpers');
|
||||
const { checkErrorResponse } = require('./lib/error-helper');
|
||||
@@ -20,7 +25,6 @@ const sysMonitor = require('./lib/sys/monitor');
|
||||
const log = require('./lib/log');
|
||||
const { makeMakeBadgeFn } = require('./lib/make-badge');
|
||||
const { QuickTextMeasurer } = require('./lib/text-measurer');
|
||||
const serverSecrets = require('./lib/server-secrets');
|
||||
const suggest = require('./lib/suggest');
|
||||
const {licenseToColor} = require('./lib/licenses');
|
||||
const { latest: latestVersion } = require('./lib/version');
|
||||
|
||||
Reference in New Issue
Block a user