Add initial sentry setup

Related to #343 but will probably need a few more changes
This commit is contained in:
Kurt Hutten
2021-06-05 20:32:56 +10:00
parent c38f94558a
commit 2e91c74baf
8 changed files with 230 additions and 4 deletions

View File

@@ -5,6 +5,7 @@
"dependencies": { "dependencies": {
"@redwoodjs/api": "^0.33.0", "@redwoodjs/api": "^0.33.0",
"@redwoodjs/api-server": "^0.33.0", "@redwoodjs/api-server": "^0.33.0",
"@sentry/node": "^6.5.1",
"cloudinary": "^1.23.0" "cloudinary": "^1.23.0"
} }
} }

View File

@@ -3,6 +3,7 @@ import {
makeMergedSchema, makeMergedSchema,
makeServices, makeServices,
} from '@redwoodjs/api' } from '@redwoodjs/api'
import { createSentryApolloPlugin } from 'src/lib/sentry'
import schemas from 'src/graphql/**/*.{js,ts}' import schemas from 'src/graphql/**/*.{js,ts}'
import services from 'src/services/**/*.{js,ts}' import services from 'src/services/**/*.{js,ts}'
@@ -16,6 +17,9 @@ export const handler = createGraphQLHandler({
schemas, schemas,
services: makeServices({ services }), services: makeServices({ services }),
}), }),
plugins: [
createSentryApolloPlugin(),
],
onException: () => { onException: () => {
// Disconnect from your database with an unhandled exception. // Disconnect from your database with an unhandled exception.
db.$disconnect() db.$disconnect()

View File

@@ -1,8 +1,9 @@
import { createUserInsecure } from 'src/services/users/users.js' import { createUserInsecure } from 'src/services/users/users.js'
import { db } from 'src/lib/db' import { db } from 'src/lib/db'
import { sentryWrapper } from 'src/lib/sentry'
import { enforceAlphaNumeric, generateUniqueString } from 'src/services/helpers' import { enforceAlphaNumeric, generateUniqueString } from 'src/services/helpers'
export const handler = async (req, _context) => { const unWrappedHandler = async (req, _context) => {
const body = JSON.parse(req.body) const body = JSON.parse(req.body)
console.log(body) console.log(body)
console.log(_context) console.log(_context)
@@ -82,3 +83,5 @@ export const handler = async (req, _context) => {
} }
} }
} }
export const handler = sentryWrapper(unWrappedHandler)

106
app/api/src/lib/sentry.ts Normal file
View File

@@ -0,0 +1,106 @@
import * as Sentry from '@sentry/node'
import { context, Config, ApolloError } from '@redwoodjs/api'
let sentryInitialized = false
if (process.env.SENTRY_DSN && !sentryInitialized) {
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.CONTEXT,
release: process.env.COMMIT_REF,
})
sentryInitialized = true
}
async function reportError(error) {
if (!sentryInitialized) return
// If you do have authentication set up, we can add
// some user data to help debug issues
// if (context.currentUser) {
// Sentry.configureScope((scope) => {
// scope.setUser({
// id: context?.currentUser?.id,
// email: context?.currentUser?.email,
// })
// })
// }
if (typeof error === 'string') {
Sentry.captureMessage(error)
} else {
Sentry.captureException(error)
}
await Sentry.flush()
}
export const sentryWrapper = (handler) => async (event, lambdaContext) => {
lambdaContext.callbackWaitsForEmptyEventLoop = false
try {
return await new Promise((resolve, reject) => {
const callback = (err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
}
}
const resp = handler(event, lambdaContext, callback)
if (resp?.then) {
resp.then(resolve, reject)
}
})
} catch (e) {
// This catches both sync errors & promise
// rejections, because we 'await' on the handler
await reportError(e)
throw e
}
}
export const createSentryApolloPlugin: Config['plugins'][number] = () => ({
requestDidStart: () => {
return {
didEncounterErrors(ctx) {
// If we couldn't parse the operation, don't
// do anything here
if (!ctx.operation) {
return;
}
for (const err of ctx.errors) {
// Only report internal server errors,
// all errors extending ApolloError should be user-facing
if (err instanceof ApolloError) {
continue;
}
// Add scoped report details and send to Sentry
Sentry.withScope(scope => {
// Annotate whether failing operation was query/mutation/subscription
scope.setTag("kind", ctx.operation.operation);
// Log query and variables as extras (make sure to strip out sensitive data!)
scope.setExtra("query", ctx.request.query);
scope.setExtra("variables", ctx.request.variables);
if (err.path) {
// We can also add the path as breadcrumb
scope.addBreadcrumb({
category: "query-path",
message: err.path.join(" > "),
level: Sentry.Severity.Debug
});
}
const transactionId = ctx.request.http.headers.get(
"x-transaction-id"
);
if (transactionId) {
scope.setTransaction(transactionId);
}
Sentry.captureException(err);
});
}
}
}
}
})

View File

@@ -8,7 +8,16 @@
[web] [web]
port = 8910 port = 8910
apiProxyPath = "/.netlify/functions" apiProxyPath = "/.netlify/functions"
includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET', 'CAD_LAMBDA_BASE_URL'] includeEnvironmentVariables = [
'GOOGLE_ANALYTICS_ID',
'CLOUDINARY_API_KEY',
'CLOUDINARY_API_SECRET',
'CAD_LAMBDA_BASE_URL',
'SENTRY_DSN',
'SENTRY_AUTH_TOKEN',
'SENTRY_ORG',
'SENTRY_PROJECT'
]
# experimentalFastRefresh = true # this seems to break cascadeStudio # experimentalFastRefresh = true # this seems to break cascadeStudio
[api] [api]
port = 8911 port = 8911
@@ -18,3 +27,10 @@
[experimental] [experimental]
esbuild = false esbuild = false
[[plugins]]
package = "@sentry/netlify-build-plugin"
[plugins.inputs]
sentryOrg = "cadhub-org"
sentryProject = "cadhub"

View File

@@ -20,6 +20,7 @@
"@redwoodjs/forms": "^0.33.0", "@redwoodjs/forms": "^0.33.0",
"@redwoodjs/router": "^0.33.0", "@redwoodjs/router": "^0.33.0",
"@redwoodjs/web": "^0.33.0", "@redwoodjs/web": "^0.33.0",
"@sentry/browser": "^6.5.1",
"browser-fs-access": "^0.17.2", "browser-fs-access": "^0.17.2",
"cloudinary-react": "^1.6.7", "cloudinary-react": "^1.6.7",
"controlkit": "^0.1.9", "controlkit": "^0.1.9",
@@ -56,4 +57,4 @@
"tailwindcss": "^2.1.2", "tailwindcss": "^2.1.2",
"worker-loader": "^3.0.7" "worker-loader": "^3.0.7"
} }
} }

View File

@@ -0,0 +1,12 @@
import { FatalErrorBoundary as FatalErrorBoundaryBase } from '@redwoodjs/web'
import * as Sentry from '@sentry/browser'
class FatalErrorBoundary extends FatalErrorBoundaryBase {
componentDidCatch(error, errorInfo) {
Sentry.withScope((scope) => {
scope.setExtras(errorInfo)
Sentry.captureException(error)
})
}
}
export default FatalErrorBoundary

View File

@@ -2985,6 +2985,84 @@
dependencies: dependencies:
any-observable "^0.3.0" any-observable "^0.3.0"
"@sentry/browser@^6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.5.1.tgz#9a6ed5607b3b0f4e83f38720e3e202906f8c5bdb"
integrity sha512-iVLCdEFwsoWAzE/hNknexPQjjDpMQV7mmaq9Z1P63bD6MfhwVTx4hG4pHn8HEvC38VvCVf1wv0v/LxtoODAYXg==
dependencies:
"@sentry/core" "6.5.1"
"@sentry/types" "6.5.1"
"@sentry/utils" "6.5.1"
tslib "^1.9.3"
"@sentry/core@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.5.1.tgz#c8b6c3ed86ed07b193c95d599c1b9a4a161e500e"
integrity sha512-Mh3sl/iUOT1myHmM6RlDy2ARzkUClx/g4DAt1rJ/IpQBOlDYQraplXSIW80i/hzRgQDfwhwgf4wUa5DicKBjKw==
dependencies:
"@sentry/hub" "6.5.1"
"@sentry/minimal" "6.5.1"
"@sentry/types" "6.5.1"
"@sentry/utils" "6.5.1"
tslib "^1.9.3"
"@sentry/hub@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.5.1.tgz#135ef09d07d32e87a53f664c0ae8fcc4f5963519"
integrity sha512-lBRMBVMYP8B4PfRiM70murbtJAXiIAao/asDEMIRNGMP6pI2ArqXfJCBYDkStukhikYD0Kqb4trXq+JYF07Hbg==
dependencies:
"@sentry/types" "6.5.1"
"@sentry/utils" "6.5.1"
tslib "^1.9.3"
"@sentry/minimal@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.5.1.tgz#b8c1b382c2ea788eec3d32d203e5081b00eb6838"
integrity sha512-q9Do/oreu1RP695CXCLowVDuQyk7ilE6FGdz2QLpTXAfx8247qOwk6+zy9Kea/Djk93+BoSDVQUSneNiVwl0nQ==
dependencies:
"@sentry/hub" "6.5.1"
"@sentry/types" "6.5.1"
tslib "^1.9.3"
"@sentry/node@^6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.5.1.tgz#a572b380858de5aeaf98eade6d8d3afcba13d364"
integrity sha512-Yh8J/QJ5e8gRBVL9VLCDpUvmiaxsxVZm0CInPHw3V/smgMkrzSKEiqxSeMq8ImPlaJrCFECqdpv4gnvYKI+mQQ==
dependencies:
"@sentry/core" "6.5.1"
"@sentry/hub" "6.5.1"
"@sentry/tracing" "6.5.1"
"@sentry/types" "6.5.1"
"@sentry/utils" "6.5.1"
cookie "^0.4.1"
https-proxy-agent "^5.0.0"
lru_map "^0.3.3"
tslib "^1.9.3"
"@sentry/tracing@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.5.1.tgz#a5f3e497d4f1f319f36475df050e135cf65af750"
integrity sha512-y1W/xFC2hAuKqSuuaovkElHY4pbli3XoXrreesg8PtO7ilX6ZbatOQbHsEsHQyoUv0F6aVA+MABOxWH2jt7tfw==
dependencies:
"@sentry/hub" "6.5.1"
"@sentry/minimal" "6.5.1"
"@sentry/types" "6.5.1"
"@sentry/utils" "6.5.1"
tslib "^1.9.3"
"@sentry/types@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.5.1.tgz#0a34ecfd1ae9275a416a105640eb4bed45a46a1d"
integrity sha512-b/7a6CMoytaeFPx4IBjfxPw3nPvsQh7ui1C8Vw0LxNNDgBwVhPLzUOWeLWbo5YZCVbGEMIWwtCUQYWxneceZSA==
"@sentry/utils@6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.5.1.tgz#046baf7d1a6564d6d555437ad3674dba9bc0806a"
integrity sha512-Wv86JYGQH+ZJ5XGFQX7h6ijl32667ikenoL9EyXMn8UoOYX/MLwZoQZin1P60wmKkYR9ifTNVmpaI9OoTaH+UQ==
dependencies:
"@sentry/types" "6.5.1"
tslib "^1.9.3"
"@sindresorhus/is@^0.14.0": "@sindresorhus/is@^0.14.0":
version "0.14.0" version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -12660,6 +12738,11 @@ lru-memoizer@^2.1.2:
lodash.clonedeep "^4.5.0" lodash.clonedeep "^4.5.0"
lru-cache "~4.0.0" lru-cache "~4.0.0"
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=
lz-string@^1.4.4: lz-string@^1.4.4:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"