Skip to content

Commit 1067868

Browse files
authored
feat(hapi): Update scope transactionName when handling request (#11448)
This PR updates the scope's `transactionName` in our Hapi error handler. Conveniently, we can access the parameterized route within our Hapi plugin, meaning we're not constrained by reading route info from a potential non-recording span.
1 parent 6c8cdcd commit 1067868

File tree

5 files changed

+59
-0
lines changed

5 files changed

+59
-0
lines changed

dev-packages/e2e-tests/test-applications/node-hapi-app/src/app.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ const init = async () => {
4444
},
4545
});
4646

47+
server.route({
48+
method: 'GET',
49+
path: '/test-error/{id}',
50+
handler: function (request) {
51+
console.log('This is an error with id', request.params.id);
52+
throw new Error(`This is an error with id ${request.params.id}`);
53+
},
54+
});
55+
4756
server.route({
4857
method: 'GET',
4958
path: '/test-failure',

dev-packages/e2e-tests/test-applications/node-hapi-app/tests/server.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,20 @@ test('Sends successful transactions to Sentry', async ({ baseURL }) => {
121121
.toBe(200);
122122
});
123123

124+
test('sends error with parameterized transaction name', async ({ baseURL }) => {
125+
const errorEventPromise = waitForError('node-hapi-app', errorEvent => {
126+
return errorEvent?.exception?.values?.[0]?.value === 'This is an error with id 123';
127+
});
128+
129+
try {
130+
await axios.get(`${baseURL}/test-error/123`);
131+
} catch {}
132+
133+
const errorEvent = await errorEventPromise;
134+
135+
expect(errorEvent?.transaction).toBe('GET /test-error/{id}');
136+
});
137+
124138
test('Sends parameterized transactions to Sentry', async ({ baseURL }) => {
125139
const pageloadTransactionEventPromise = waitForTransaction('node-hapi-app', transactionEvent => {
126140
return (

dev-packages/node-integration-tests/suites/tracing/hapi/scenario.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ const init = async () => {
3535
},
3636
});
3737

38+
server.route({
39+
method: 'GET',
40+
path: '/error/{id}',
41+
handler: (_request, _h) => {
42+
return new Error('Sentry Test Error');
43+
},
44+
});
45+
3846
server.route({
3947
method: 'GET',
4048
path: '/boom-error',

dev-packages/node-integration-tests/suites/tracing/hapi/test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ describe('hapi auto-instrumentation', () => {
5656
.makeRequest('get', '/error');
5757
});
5858

59+
test('CJS - should assign parameterized transactionName to error.', done => {
60+
createRunner(__dirname, 'scenario.js')
61+
.expect({
62+
event: {
63+
...EXPECTED_ERROR_EVENT,
64+
transaction: 'GET /error/{id}',
65+
},
66+
})
67+
.ignore('transaction')
68+
.expectError()
69+
.start(done)
70+
.makeRequest('get', '/error/123');
71+
});
72+
5973
test('CJS - should handle returned Boom errors in routes.', done => {
6074
createRunner(__dirname, 'scenario.js')
6175
.expect({

packages/node/src/integrations/tracing/hapi/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import {
66
captureException,
77
defineIntegration,
88
getActiveSpan,
9+
getDefaultIsolationScope,
10+
getIsolationScope,
911
getRootSpan,
1012
} from '@sentry/core';
1113
import type { IntegrationFn } from '@sentry/types';
14+
import { logger } from '@sentry/utils';
15+
import { DEBUG_BUILD } from '../../../debug-build';
1216
import type { Boom, RequestEvent, ResponseObject, Server } from './types';
1317

1418
const _hapiIntegration = (() => {
@@ -58,6 +62,16 @@ export const hapiErrorPlugin = {
5862
const server = serverArg as unknown as Server;
5963

6064
server.events.on('request', (request, event) => {
65+
if (getIsolationScope() !== getDefaultIsolationScope()) {
66+
const route = request.route;
67+
if (route && route.path) {
68+
getIsolationScope().setTransactionName(`${route.method?.toUpperCase() || 'GET'} ${route.path}`);
69+
}
70+
} else {
71+
DEBUG_BUILD &&
72+
logger.warn('Isolation scope is still the default isolation scope - skipping setting transactionName');
73+
}
74+
6175
const activeSpan = getActiveSpan();
6276
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
6377

0 commit comments

Comments
 (0)