forked from alex-ppg/fastify-sentry
-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
106 lines (93 loc) · 2.96 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
'use strict'
const fp = require('fastify-plugin')
const Sentry = require('@sentry/node')
const Tracing = require('@sentry/tracing') // eslint-disable-line no-unused-vars
const extractRequestData = require('./lib/extractRequestData.js')
/**
* @param {import('fastify').fastify} fastify
* @param {{
* dsn: string,
* tracing: boolean = false,
* errorHandler?: (error: Error, request: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply) => void,
* errorFilter?: (error: Error, request: import('fastify').FastifyRequest) => boolean,
* integrations?: Sentry.NodeOptions['integrations']
* }} opts
* @param {function} next
* @returns {void}
*/
function sentryConnector(fastify, opts, next) {
if (!opts || !opts.dsn) {
return next(new Error('Sentry DSN is required.'))
}
if (opts.tracing === true) {
opts.integrations = [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({
fastify
}),
...(opts.integrations ?? [])
]
}
Sentry.init(opts)
fastify.decorateRequest('sentryTransaction', null)
fastify.addHook('onRequest', async (request) => {
// If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision)
let traceparentData
if (
request.headers &&
typeof request.headers['sentry-trace'] === 'string'
) {
traceparentData = Tracing.extractTraceparentData(
request.headers['sentry-trace']
)
}
const extractedRequestData = extractRequestData(request)
request.sentryTransaction = Sentry.startTransaction(
{
op: 'http.server',
name: `${request.method} ${request.url}`,
...traceparentData
},
{ request: extractedRequestData }
)
return
})
fastify.addHook('onResponse', async (request, reply) => {
setImmediate(() => {
const transaction = request.sentryTransaction
transaction.setData('url', request.url)
transaction.setData('query', request.query)
transaction.setHttpStatus(reply.statusCode)
transaction.finish()
})
return
})
fastify.setErrorHandler((error, request, reply) => {
if (opts.errorFilter && !opts.errorFilter(error, request)) {
process.env.NODE_ENV !== 'production' &&
console.warn('Error not reported to Sentry', error)
} else {
Sentry.withScope((scope) => {
if (request && request.user && request.user.sub) {
scope.setUser({
id: request.user.sub,
ip_address: request.ip
})
} else {
scope.setUser({
ip_address: request.ip
})
}
scope.setTag('path', request.url)
// will be tagged with my-tag="my value"
Sentry.captureException(error)
})
}
opts.errorHandler
? opts.errorHandler(error, request, reply)
: reply.send(error)
return
})
next()
}
module.exports = fp(sentryConnector, { name: 'fastify-sentry' })