Add Sentry for api

pull/2163/head
Jérémy Rialland 2022-06-08 11:09:34 +02:00 committed by Johan Girod
parent ff2b3bf207
commit aebfce578c
4 changed files with 179 additions and 2 deletions

View File

@ -32,6 +32,8 @@
"@koa/cors": "^3.3.0",
"@koa/router": "^10.1.1",
"@publicodes/api": "^1.0.0-beta.42",
"@sentry/node": "^7.1.1",
"@sentry/tracing": "^7.1.1",
"koa": "^2.13.4",
"koa-body": "^5.0.0",
"koa-static": "^5.0.0",

View File

@ -1,5 +1,5 @@
import cors from '@koa/cors'
import Router from '@koa/router'
import Router, { RouterContext } from '@koa/router'
import { koaMiddleware as publicodesAPI } from '@publicodes/api'
import Koa from 'koa'
import rules from 'modele-social'
@ -7,6 +7,7 @@ import Engine from 'publicodes'
import openapi from './openapi.json' assert { type: 'json' }
import { docRoutes } from './route/doc.js'
import { openapiRoutes } from './route/openapi.js'
import Sentry, { requestHandler, tracingMiddleWare } from './sentry.js'
type State = Koa.DefaultState
type Context = Koa.DefaultContext
@ -14,6 +15,18 @@ type Context = Koa.DefaultContext
const app = new Koa<State, Context>()
const router = new Router<State, Context>()
app.use(requestHandler)
app.use(tracingMiddleWare)
app.on('error', (err, ctx: RouterContext) => {
Sentry.withScope((scope) => {
scope.addEventProcessor((event) => {
return Sentry.Handlers.parseRequest(event, ctx.request)
})
Sentry.captureException(err)
})
})
app.use(cors())
const apiRoutes = publicodesAPI(new Engine(rules))

85
api/source/sentry.ts Normal file
View File

@ -0,0 +1,85 @@
import { RouterContext } from '@koa/router'
import * as Sentry from '@sentry/node'
import * as Tracing from '@sentry/tracing'
// eslint-disable-next-line n/no-deprecated-api
import Domains from 'domain'
import { Context, Next } from 'koa'
Sentry.init({
dsn: 'https://21ddaba2424b46b4b14185dba51b1288@sentry.incubateur.net/42',
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 0.5,
})
export default Sentry
// not mandatory, but adding domains does help a lot with breadcrumbs
export const requestHandler = (ctx: Context, next: Next) => {
return new Promise<void>((resolve, reject) => {
const local = Domains.create()
local.add(ctx as never)
local.on('error', (err) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
ctx.status = (err.status as number) || 500
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
ctx.body = err.message
ctx.app.emit('error', err, ctx)
})
void local.run(async () => {
Sentry.getCurrentHub().configureScope((scope) =>
scope.addEventProcessor((event) =>
Sentry.Handlers.parseRequest(event, ctx.request, { user: false })
)
)
await next()
resolve()
})
})
}
// this tracing middleware creates a transaction per request
export const tracingMiddleWare = async (ctx: RouterContext, next: Next) => {
const reqMethod = (ctx.method || '').toUpperCase()
const reqUrl = ctx.url && Tracing.stripUrlQueryAndFragment(ctx.url)
// connect to trace of upstream app
let traceparentData
if (ctx.request.get('sentry-trace')) {
traceparentData = Tracing.extractTraceparentData(
ctx.request.get('sentry-trace')
)
}
const transaction = Sentry.startTransaction({
name: `${reqMethod} ${reqUrl}`,
op: 'http.server',
...traceparentData,
})
ctx.__sentry_transaction = transaction
// We put the transaction on the scope so users can attach children to it
Sentry.getCurrentHub().configureScope((scope) => {
scope.setSpan(transaction)
})
ctx.res.on('finish', () => {
// Push `transaction.finish` to the next event loop so open spans have a chance to finish before the transaction closes
setImmediate(() => {
// if using koa router, a nicer way to capture transaction using the matched route
if (ctx._matchedRoute) {
const mountPath = (ctx.mountPath as undefined | string) || ''
transaction.setName(
`${reqMethod} ${mountPath}${ctx._matchedRoute.toString()}`
)
}
transaction.setHttpStatus(ctx.status)
transaction.finish()
})
})
await next()
}

View File

@ -4369,6 +4369,18 @@ __metadata:
languageName: node
linkType: hard
"@sentry/core@npm:7.1.1":
version: 7.1.1
resolution: "@sentry/core@npm:7.1.1"
dependencies:
"@sentry/hub": 7.1.1
"@sentry/types": 7.1.1
"@sentry/utils": 7.1.1
tslib: ^1.9.3
checksum: 687d46e7a0960cf595ae7ce392aaad2157653513cd1f92bf2bbcbfe6663365fab874a53f63220fcf1ad1312abe27bc871bc1e84abe5b60cd661642e661be4b6c
languageName: node
linkType: hard
"@sentry/hub@npm:6.19.6":
version: 6.19.6
resolution: "@sentry/hub@npm:6.19.6"
@ -4380,6 +4392,17 @@ __metadata:
languageName: node
linkType: hard
"@sentry/hub@npm:7.1.1":
version: 7.1.1
resolution: "@sentry/hub@npm:7.1.1"
dependencies:
"@sentry/types": 7.1.1
"@sentry/utils": 7.1.1
tslib: ^1.9.3
checksum: ddc61b31aa0f45ee9ea3f32173d6e49fa93fa8b83eb7d5ab0c53758f31731e1ae70a0730212ed3a0889ea47372e5919f511ad7c056dae2c18d31049caeb0c38c
languageName: node
linkType: hard
"@sentry/integrations@npm:^6.19.6":
version: 6.19.6
resolution: "@sentry/integrations@npm:6.19.6"
@ -4403,6 +4426,22 @@ __metadata:
languageName: node
linkType: hard
"@sentry/node@npm:^7.1.1":
version: 7.1.1
resolution: "@sentry/node@npm:7.1.1"
dependencies:
"@sentry/core": 7.1.1
"@sentry/hub": 7.1.1
"@sentry/types": 7.1.1
"@sentry/utils": 7.1.1
cookie: ^0.4.1
https-proxy-agent: ^5.0.0
lru_map: ^0.3.3
tslib: ^1.9.3
checksum: ecda1b11356da01fbfbf0909fa7eead19c442ff6ef7bce9f7a5fadcdc220399014a42ac08614d6fa03e555084de00c0ef8904fb45e0682010ef5a8e2920692f9
languageName: node
linkType: hard
"@sentry/react@npm:^6.19.6":
version: 6.19.6
resolution: "@sentry/react@npm:6.19.6"
@ -4432,6 +4471,18 @@ __metadata:
languageName: node
linkType: hard
"@sentry/tracing@npm:^7.1.1":
version: 7.1.1
resolution: "@sentry/tracing@npm:7.1.1"
dependencies:
"@sentry/hub": 7.1.1
"@sentry/types": 7.1.1
"@sentry/utils": 7.1.1
tslib: ^1.9.3
checksum: 3b11cc36eaf99c716185ccd80a0ec2e5b042a38cf4419ce5a0eeda7f81c7d097134e94c4aa6dfa45b23c83d8563b20ac6ad9cd47bbd164f363039672b971b87d
languageName: node
linkType: hard
"@sentry/types@npm:6.19.6":
version: 6.19.6
resolution: "@sentry/types@npm:6.19.6"
@ -4439,6 +4490,13 @@ __metadata:
languageName: node
linkType: hard
"@sentry/types@npm:7.1.1":
version: 7.1.1
resolution: "@sentry/types@npm:7.1.1"
checksum: 3f1b43782ca93c6520589b3739d4833cd00afb0f3ff72f5afaa42d4224c63163b1ef6bd167143b7d72667d0711f09fc4a42d36418ee242898a9cbd979dbac65d
languageName: node
linkType: hard
"@sentry/utils@npm:6.19.6":
version: 6.19.6
resolution: "@sentry/utils@npm:6.19.6"
@ -4449,6 +4507,16 @@ __metadata:
languageName: node
linkType: hard
"@sentry/utils@npm:7.1.1":
version: 7.1.1
resolution: "@sentry/utils@npm:7.1.1"
dependencies:
"@sentry/types": 7.1.1
tslib: ^1.9.3
checksum: b36d0e03b007229cad81dc8db517c4bf58c8e7636aa80a575dc91fb605028c1a0b61e5f090704e04b5e9021fd8ec95b1a6f1465449f2a1a6c657b283295add71
languageName: node
linkType: hard
"@sideway/address@npm:^4.1.3":
version: 4.1.4
resolution: "@sideway/address@npm:4.1.4"
@ -7770,6 +7838,8 @@ __metadata:
"@koa/cors": ^3.3.0
"@koa/router": ^10.1.1
"@publicodes/api": ^1.0.0-beta.42
"@sentry/node": ^7.1.1
"@sentry/tracing": ^7.1.1
"@types/koa": ^2.13.4
"@types/koa-static": ^4.0.2
"@types/koa__cors": ^3.3.0
@ -9882,7 +9952,7 @@ __metadata:
languageName: node
linkType: hard
"cookie@npm:0.4.2":
"cookie@npm:0.4.2, cookie@npm:^0.4.1":
version: 0.4.2
resolution: "cookie@npm:0.4.2"
checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b
@ -15884,6 +15954,13 @@ __metadata:
languageName: node
linkType: hard
"lru_map@npm:^0.3.3":
version: 0.3.3
resolution: "lru_map@npm:0.3.3"
checksum: ca9dd43c65ed7a4f117c548028101c5b6855e10923ea9d1f635af53ad20c5868ff428c364d454a7b57fe391b89c704982275410c3c5099cca5aeee00d76e169a
languageName: node
linkType: hard
"lz-string@npm:^1.4.4":
version: 1.4.4
resolution: "lz-string@npm:1.4.4"