2023-06-15 13:57:48 +00:00
|
|
|
// eslint-disable-next-line n/no-deprecated-api
|
|
|
|
import Domains from 'domain'
|
|
|
|
|
2022-06-08 09:09:34 +00:00
|
|
|
import { RouterContext } from '@koa/router'
|
|
|
|
import * as Sentry from '@sentry/node'
|
|
|
|
import * as Tracing from '@sentry/tracing'
|
|
|
|
import { Context, Next } from 'koa'
|
|
|
|
|
2022-08-30 15:35:01 +00:00
|
|
|
const release =
|
|
|
|
(process.env.APP ?? '') +
|
|
|
|
'-' +
|
|
|
|
(process.env.CONTAINER_VERSION ?? '').substring(0, 7)
|
|
|
|
|
2022-06-08 09:09:34 +00:00
|
|
|
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,
|
2022-08-30 15:35:01 +00:00
|
|
|
release,
|
|
|
|
environment: release.includes('api-pr') ? 'test' : 'production',
|
2022-06-08 09:09:34 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
export default Sentry
|
|
|
|
|
|
|
|
// not mandatory, but adding domains does help a lot with breadcrumbs
|
|
|
|
export const requestHandler = (ctx: Context, next: Next) => {
|
2022-06-08 09:57:10 +00:00
|
|
|
return new Promise<void>((resolve) => {
|
2022-06-08 09:09:34 +00:00
|
|
|
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()
|
|
|
|
}
|