Skip to content

Commit a1bb8d0

Browse files
authored
feat(koa): Update scope transactionName when creating router span (#11476)
Update the current scope's `transactionName` in our Koa integration. Unfortunately, I didn't find a reliable way to get the parameterized route in our error handler/via `app.use` because similarly to Express, koa doesn't seem to expose this information before going through the actual route layers.
1 parent 5542127 commit a1bb8d0

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

dev-packages/e2e-tests/test-applications/node-koa-app/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ router1.get('/test-exception', async ctx => {
7575
throw new Error('This is an exception');
7676
});
7777

78+
router1.get('/test-exception/:id', async ctx => {
79+
throw new Error(`This is an exception with id ${ctx.params.id}`);
80+
});
81+
7882
router1.get('/test-outgoing-fetch-external-allowed', async ctx => {
7983
const fetchResponse = await fetch(`http://localhost:${port2}/external-allowed`);
8084
const data = await fetchResponse.json();

dev-packages/e2e-tests/test-applications/node-koa-app/tests/errors.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,28 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
4141

4242
test('Sends correct error event', async ({ baseURL }) => {
4343
const errorEventPromise = waitForError('node-koa-app', event => {
44-
return !event.type && event.exception?.values?.[0]?.value === 'This is an exception';
44+
return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123';
4545
});
4646

4747
try {
48-
await axios.get(`${baseURL}/test-exception`);
48+
await axios.get(`${baseURL}/test-exception/123`);
4949
} catch {
5050
// this results in an error, but we don't care - we want to check the error event
5151
}
5252

5353
const errorEvent = await errorEventPromise;
5454

5555
expect(errorEvent.exception?.values).toHaveLength(1);
56-
expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception');
56+
expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123');
5757

5858
expect(errorEvent.request).toEqual({
5959
method: 'GET',
6060
cookies: {},
6161
headers: expect.any(Object),
62-
url: 'http://localhost:3030/test-exception',
62+
url: 'http://localhost:3030/test-exception/123',
6363
});
6464

65-
expect(errorEvent.transaction).toEqual('GET /test-exception');
65+
expect(errorEvent.transaction).toEqual('GET /test-exception/:id');
6666

6767
expect(errorEvent.contexts?.trace).toEqual({
6868
trace_id: expect.any(String),

packages/node/src/integrations/tracing/koa.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
import { registerInstrumentations } from '@opentelemetry/instrumentation';
22
import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa';
3-
import { captureException, defineIntegration } from '@sentry/core';
3+
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
4+
import {
5+
captureException,
6+
defineIntegration,
7+
getDefaultIsolationScope,
8+
getIsolationScope,
9+
spanToJSON,
10+
} from '@sentry/core';
411
import type { IntegrationFn } from '@sentry/types';
12+
import { logger } from '@sentry/utils';
13+
import { DEBUG_BUILD } from '../../debug-build';
514

615
const _koaIntegration = (() => {
716
return {
817
name: 'Koa',
918
setupOnce() {
1019
registerInstrumentations({
11-
instrumentations: [new KoaInstrumentation()],
20+
instrumentations: [
21+
new KoaInstrumentation({
22+
requestHook(span, info) {
23+
if (getIsolationScope() === getDefaultIsolationScope()) {
24+
DEBUG_BUILD &&
25+
logger.warn('Isolation scope is default isolation scope - skipping setting transactionName');
26+
return;
27+
}
28+
const attributes = spanToJSON(span).data;
29+
const route = attributes && attributes[SemanticAttributes.HTTP_ROUTE];
30+
const method = info.context.request.method.toUpperCase() || 'GET';
31+
if (route) {
32+
getIsolationScope().setTransactionName(`${method} ${route}`);
33+
}
34+
},
35+
}),
36+
],
1237
});
1338
},
1439
};

0 commit comments

Comments
 (0)