Skip to content

Commit 9e47a18

Browse files
Merge pull request #11 from zentered/fix/trace-data
fix: add missing request to trace'
2 parents a34c82b + 8df5578 commit 9e47a18

File tree

7 files changed

+111
-4
lines changed

7 files changed

+111
-4
lines changed

index.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const fp = require('fastify-plugin')
44
const Sentry = require('@sentry/node')
55
const Tracing = require('@sentry/tracing') // eslint-disable-line no-unused-vars
6+
const extractRequestData = require('./lib/extractRequestData.js')
67

78
function sentryConnector(fastify, opts, next) {
89
if (!opts || !opts.dsn) {
@@ -22,10 +23,27 @@ function sentryConnector(fastify, opts, next) {
2223

2324
fastify.decorateRequest('sentryTransaction', null)
2425
fastify.addHook('onRequest', async (request) => {
25-
request.sentryTransaction = Sentry.startTransaction({
26-
op: 'request',
27-
name: `${request.method} ${request.url}`
28-
})
26+
// If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision)
27+
let traceparentData
28+
if (
29+
request.headers &&
30+
typeof request.headers['sentry-trace'] === 'string'
31+
) {
32+
traceparentData = Tracing.extractTraceparentData(
33+
request.headers['sentry-trace']
34+
)
35+
}
36+
37+
const extractedRequestData = extractRequestData(request)
38+
39+
request.sentryTransaction = Sentry.startTransaction(
40+
{
41+
op: 'request',
42+
name: `${request.method} ${request.url}`,
43+
...traceparentData
44+
},
45+
{ request: extractedRequestData }
46+
)
2947
return
3048
})
3149

lib/extractRequestData.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict'
2+
3+
const DEFAULT_REQUEST_KEYS = ['headers', 'method', 'query_string', 'url']
4+
5+
/**
6+
* Function copied from
7+
* https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts
8+
* and mofidied for Fastify
9+
*
10+
* Data (req.body) isn't available in onRequest hook,
11+
* as it is parsed later in the fastify lifecycle
12+
* https://www.fastify.io/docs/latest/Hooks/#onrequest
13+
*/
14+
function extractRequestData(req, keys) {
15+
if (!keys || keys.length <= 0 || typeof keys !== 'object') {
16+
keys = DEFAULT_REQUEST_KEYS
17+
}
18+
const requestData = {}
19+
const headers = req.headers || {}
20+
const method = req.method
21+
const host = req.hostname
22+
const protocol = req.protocol
23+
const originalUrl = req.url
24+
const absoluteUrl = protocol + '://' + host + originalUrl
25+
keys.forEach(function (key) {
26+
switch (key) {
27+
case 'headers':
28+
requestData.headers = headers
29+
break
30+
case 'method':
31+
requestData.method = method
32+
break
33+
case 'url':
34+
requestData.url = absoluteUrl
35+
break
36+
case 'query_string':
37+
requestData.query_string = Object.assign({}, req.query)
38+
break
39+
default:
40+
if ({}.hasOwnProperty.call(req, key)) {
41+
requestData[key] = req[key]
42+
}
43+
}
44+
})
45+
return requestData
46+
}
47+
48+
module.exports = exports = extractRequestData
File renamed without changes.
File renamed without changes.
File renamed without changes.

test/error-handler.js renamed to test/error-handler.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ tap.test('sentryConnector with custom error handler', async (test) => {
3030

3131
const response = await fastify.inject({
3232
method: 'POST',
33+
headers: { 'sentry-trace': 'testing trace' },
3334
url: '/',
3435
payload: { error: 503, message: 'Internal Server Error' }
3536
})

test/extractRequestData.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict'
2+
3+
/* eslint-disable node/no-unpublished-require */
4+
const tap = require('tap')
5+
const fn = require('../lib/extractRequestData')
6+
7+
tap.test('extractedRequestData should process fastify request', (test) => {
8+
test.plan(1)
9+
const request = {
10+
headers: {
11+
host: 'localhost:3000',
12+
accept: '/*/'
13+
},
14+
hostname: 'localhost:3000',
15+
protocol: 'http',
16+
method: 'GET',
17+
url: '/testing'
18+
}
19+
20+
const expected = {
21+
headers: { host: 'localhost:3000', accept: '/*/' },
22+
method: 'GET',
23+
query_string: {},
24+
url: 'http://localhost:3000/testing'
25+
}
26+
27+
const actual = fn(request)
28+
test.strictSame(actual, expected)
29+
})
30+
31+
tap.test('extractedRequestData should process other keys', (test) => {
32+
test.plan(2)
33+
const request = {
34+
testing: 'testing'
35+
}
36+
37+
const actual = fn(request, ['testing', 'testing2'])
38+
test.equal(actual.testing, 'testing')
39+
test.equal(actual.testing2, undefined)
40+
})

0 commit comments

Comments
 (0)