Integrated Sentry error tracking

no issue

- this allows tracking of application errors within Sentry
- only enabled for HTTP 500 errors for now
- it is disabled by default
This commit is contained in:
Daniel Lockyer 2020-01-29 12:09:21 +00:00
parent e200914dec
commit 7751e78c98
13 changed files with 166 additions and 3 deletions

30
core/server/sentry.js Normal file
View File

@ -0,0 +1,30 @@
const config = require('./config');
const sentryConfig = config.get('sentry');
const expressNoop = function (req, res, next) {
next();
};
if (sentryConfig && !sentryConfig.disabled) {
const Sentry = require('@sentry/node');
const version = require('../../package.json').version;
Sentry.init({
dsn: sentryConfig.dsn,
release: 'ghost@' + version
});
module.exports = {
requestHandler: Sentry.Handlers.requestHandler(),
errorHandler: Sentry.Handlers.errorHandler({
shouldHandleError(error) {
// Only handle 500 errors for now
return (error.statusCode === 500);
}
})
};
} else {
module.exports = {
requestHandler: expressNoop,
errorHandler: expressNoop
};
}

View File

@ -6,10 +6,12 @@ const constants = require('../../lib/constants');
const urlUtils = require('../../lib/url-utils');
const shared = require('../shared');
const adminMiddleware = require('./middleware');
const sentry = require('../../sentry');
module.exports = function setupAdminApp() {
debug('Admin setup start');
const adminApp = express();
adminApp.use(sentry.requestHandler);
// Make sure 'req.secure' and `req.hostname` is valid for proxied requests
// (X-Forwarded-Proto header will be checked, if present)
@ -50,6 +52,7 @@ module.exports = function setupAdminApp() {
// Finally, routing
adminApp.get('*', require('./controller'));
adminApp.use(sentry.errorHandler);
adminApp.use(shared.middlewares.errorHandler.pageNotFound);
adminApp.use(shared.middlewares.errorHandler.handleHTMLResponse);

View File

@ -4,10 +4,12 @@ const express = require('express');
const bodyParser = require('body-parser');
const shared = require('../../../shared');
const routes = require('./routes');
const sentry = require('../../../../sentry');
module.exports = function setupApiApp() {
debug('Admin API canary setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// API middleware
@ -32,6 +34,7 @@ module.exports = function setupApiApp() {
apiApp.use(routes());
// API error handling
apiApp.use(sentry.errorHandler);
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponseV2);

View File

@ -4,10 +4,12 @@ const bodyParser = require('body-parser');
const express = require('express');
const shared = require('../../../shared');
const routes = require('./routes');
const sentry = require('../../../../sentry');
module.exports = function setupApiApp() {
debug('Content API canary setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// API middleware
@ -27,6 +29,7 @@ module.exports = function setupApiApp() {
apiApp.use(routes());
// API error handling
apiApp.use(sentry.errorHandler);
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponse);

View File

@ -6,10 +6,12 @@ const membersService = require('../../../../services/members');
const urlUtils = require('../../../../lib/url-utils');
const labs = require('../../../shared/middlewares/labs');
const shared = require('../../../shared');
const sentry = require('../../../../sentry');
module.exports = function setupMembersApiApp() {
debug('Members API canary setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// Entire app is behind labs flag
apiApp.use(labs.members);
@ -24,6 +26,7 @@ module.exports = function setupMembersApiApp() {
apiApp.put('/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next));
// API error handling
apiApp.use(sentry.errorHandler);
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponseV2);

View File

@ -2,10 +2,12 @@ const debug = require('ghost-ignition').debug('web:api:default:app');
const express = require('express');
const urlUtils = require('../../lib/url-utils');
const errorHandler = require('../shared/middlewares/error-handler');
const sentry = require('../../sentry');
module.exports = function setupApiApp() {
debug('Parent API setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// Mount different API versions
apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'content'}), require('./v2/content/app')());
@ -20,6 +22,7 @@ module.exports = function setupApiApp() {
apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'members'}), require('./canary/members/app')());
// Error handling for requests to non-existent API versions
apiApp.use(sentry.errorHandler);
apiApp.use(errorHandler.resourceNotFound);
apiApp.use(errorHandler.handleJSONResponse);

View File

@ -4,10 +4,12 @@ const express = require('express');
const bodyParser = require('body-parser');
const shared = require('../../../shared');
const routes = require('./routes');
const sentry = require('../../../../sentry');
module.exports = function setupApiApp() {
debug('Admin API v2 setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// API middleware
@ -32,6 +34,7 @@ module.exports = function setupApiApp() {
apiApp.use(routes());
// API error handling
apiApp.use(sentry.errorHandler);
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponseV2);

View File

@ -4,10 +4,12 @@ const bodyParser = require('body-parser');
const express = require('express');
const shared = require('../../../shared');
const routes = require('./routes');
const sentry = require('../../../../sentry');
module.exports = function setupApiApp() {
debug('Content API v2 setup start');
const apiApp = express();
apiApp.use(sentry.requestHandler);
// API middleware
@ -27,6 +29,7 @@ module.exports = function setupApiApp() {
apiApp.use(routes());
// API error handling
apiApp.use(sentry.errorHandler);
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponse);

View File

@ -9,12 +9,14 @@ const escapeRegExp = require('lodash.escaperegexp');
const {URL} = require('url');
const urlUtils = require('../lib/url-utils');
const storage = require('../adapters/storage');
const sentry = require('../sentry');
const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
module.exports = function setupParentApp(options = {}) {
debug('ParentApp setup start');
const parentApp = express();
parentApp.use(sentry.requestHandler);
// ## Global settings
@ -53,6 +55,7 @@ module.exports = function setupParentApp(options = {}) {
// Wrap the admin and API apps into a single express app for use with vhost
const adminApp = express();
adminApp.use(sentry.requestHandler);
adminApp.enable('trust proxy'); // required to respect x-forwarded-proto in admin requests
adminApp.use('/ghost/api', require('./api')());
adminApp.use('/ghost', require('./admin')());

View File

@ -19,6 +19,7 @@ const membersService = require('../../services/members');
const membersMiddleware = membersService.middleware;
const siteRoutes = require('./routes');
const shared = require('../shared');
const sentry = require('../../sentry');
const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
@ -76,6 +77,7 @@ module.exports = function setupSiteApp(options = {}) {
debug('Site setup start');
const siteApp = express();
siteApp.use(sentry.requestHandler);
// Make sure 'req.secure' is valid for proxied requests
// (X-Forwarded-Proto header will be checked, if present)
@ -193,6 +195,7 @@ module.exports = function setupSiteApp(options = {}) {
siteApp.use(SiteRouter);
// ### Error handlers
siteApp.use(sentry.errorHandler);
siteApp.use(shared.middlewares.errorHandler.pageNotFound);
siteApp.use(shared.middlewares.errorHandler.handleThemeResponse);

View File

@ -3,6 +3,7 @@
var startTime = Date.now(),
debug = require('ghost-ignition').debug('boot:index'),
sentry = require('./core/server/sentry'),
ghost, express, common, urlService, parentApp;
debug('First requires...');
@ -16,6 +17,8 @@ common = require('./core/server/lib/common');
urlService = require('./core/frontend/services/url');
parentApp = express();
parentApp.use(sentry.requestHandler);
debug('Initialising Ghost');
ghost().then(function (ghostServer) {
// Mount our Ghost instance on our desired subdirectory path if it exists.

View File

@ -40,6 +40,7 @@
},
"dependencies": {
"@nexes/nql": "0.3.0",
"@sentry/node": "5.11.1",
"@tryghost/helpers": "1.1.22",
"@tryghost/members-api": "0.12.0",
"@tryghost/members-ssr": "0.7.4",

108
yarn.lock
View File

@ -148,6 +148,85 @@
component-type "^1.2.1"
join-component "^1.1.0"
"@sentry/apm@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.11.1.tgz#cc89fa4150056fbf009f92eca94fccc3980db34e"
integrity sha512-4iZH11p/7w9IMLT9hqNY1+EqLESltiIoF6/YsbpK93sXWGEs8VQ83IuvGuKWxajvHgDmj4ND0TxIliTsYqTqFw==
dependencies:
"@sentry/browser" "5.11.1"
"@sentry/hub" "5.11.1"
"@sentry/minimal" "5.11.1"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/browser@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.11.1.tgz#337ffcb52711b23064c847a07629e966f54a5ebb"
integrity sha512-oqOX/otmuP92DEGRyZeBuQokXdeT9HQRxH73oqIURXXNLMP3PWJALSb4HtT4AftEt/2ROGobZLuA4TaID6My/Q==
dependencies:
"@sentry/core" "5.11.1"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/core@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.11.1.tgz#9e2da485e196ae32971545c1c49ee6fe719930e2"
integrity sha512-BpvPosVNT20Xso4gAV54Lu3KqDmD20vO63HYwbNdST5LUi8oYV4JhvOkoBraPEM2cbBwQvwVcFdeEYKk4tin9A==
dependencies:
"@sentry/hub" "5.11.1"
"@sentry/minimal" "5.11.1"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/hub@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.11.1.tgz#ddcb865563fae53852d405885c46b4c6de68a91b"
integrity sha512-ucKprYCbGGLLjVz4hWUqHN9KH0WKUkGf5ZYfD8LUhksuobRkYVyig0ZGbshECZxW5jcDTzip4Q9Qimq/PkkXBg==
dependencies:
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
tslib "^1.9.3"
"@sentry/minimal@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.11.1.tgz#0e705d01a567282d8fbbda2aed848b4974cc3cec"
integrity sha512-HK8zs7Pgdq7DsbZQTThrhQPrJsVWzz7MaluAbQA0rTIAJ3TvHKQpsVRu17xDpjZXypqWcKCRsthDrC4LxDM1Bg==
dependencies:
"@sentry/hub" "5.11.1"
"@sentry/types" "5.11.0"
tslib "^1.9.3"
"@sentry/node@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.11.1.tgz#2a9c18cd1209cfdf7a69b9d91303413149d2c910"
integrity sha512-FbJs0blJ36gEzE0rc2yBfA/KE+kXOLl8MUfFTcyJCBdCGF8XMETDCmgINnJ4TyBUJviwKoPw2TCk9TL2pa/A1w==
dependencies:
"@sentry/apm" "5.11.1"
"@sentry/core" "5.11.1"
"@sentry/hub" "5.11.1"
"@sentry/types" "5.11.0"
"@sentry/utils" "5.11.1"
cookie "^0.3.1"
https-proxy-agent "^4.0.0"
lru_map "^0.3.3"
tslib "^1.9.3"
"@sentry/types@5.11.0":
version "5.11.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.11.0.tgz#40f0f3174362928e033ddd9725d55e7c5cb7c5b6"
integrity sha512-1Uhycpmeo1ZK2GLvrtwZhTwIodJHcyIS6bn+t4IMkN9MFoo6ktbAfhvexBDW/IDtdLlCGJbfm8nIZerxy0QUpg==
"@sentry/utils@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.11.1.tgz#aa19fcc234cf632257b2281261651d2fac967607"
integrity sha512-O0Zl4R2JJh8cTkQ8ZL2cDqGCmQdpA5VeXpuBbEl1v78LQPkBDISi35wH4mKmLwMsLBtTVpx2UeUHBj0KO5aLlA==
dependencies:
"@sentry/types" "5.11.0"
tslib "^1.9.3"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@ -431,6 +510,11 @@ agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0:
dependencies:
es6-promisify "^5.0.0"
agent-base@5:
version "5.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
agent-base@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
@ -911,12 +995,12 @@ bluebird@3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
bluebird@3.7.2:
bluebird@3.7.2, bluebird@^3.4.3, bluebird@^3.7.0:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bluebird@^3.4.1, bluebird@^3.4.3, bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.5.4, bluebird@^3.5.5, bluebird@^3.7.0:
bluebird@^3.4.1, bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.5.4, bluebird@^3.5.5:
version "3.7.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.0.tgz#56a6a886e03f6ae577cffedeb524f8f2450293cf"
integrity sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==
@ -1758,6 +1842,11 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookie@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
cookiejar@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
@ -4161,6 +4250,14 @@ https-proxy-agent@^3.0.0:
agent-base "^4.3.0"
debug "^3.1.0"
https-proxy-agent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
dependencies:
agent-base "5"
debug "4"
iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -5371,6 +5468,11 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
lru_map@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=
mailcomposer@~0.2.10:
version "0.2.12"
resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-0.2.12.tgz#4d02a604616adcb45fb36d37513f4c1bd0b75681"
@ -8821,7 +8923,7 @@ truncate@~2.1.0:
resolved "https://registry.yarnpkg.com/truncate/-/truncate-2.1.0.tgz#391183563a25cffbd4d613a1d00ae5844c9e55d3"
integrity sha512-em3E3SUDONOjTBcZ36DTm3RvDded3IRU9rX32oHwwXNt3rJD5MVaFlJTQvs8tJoHRoeYP36OuQ1eL/Q7bNEWIQ==
tslib@^1.9.0:
tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==