Skip to content

Commit 868c7fc

Browse files
msonnbclaude
andauthored
feat!: Use handler span op for terminal request handlers (#22871)
Collapses the terminal request-handler span ops across the server frameworks to the cross-framework `handler` op, so every "this is where the user's route handler runs" span shares one op regardless of framework. Hono’s app.request() is an internal HTTP dispatch (one sub-app calling another in-process), so it is semantically an inbound server request and is now traced as `http.server` Part of #22446 --------- Co-authored-by: Claude Opus 4.8 <[email protected]>
1 parent 7a18fe8 commit 868c7fc

36 files changed

Lines changed: 117 additions & 100 deletions

File tree

‎dev-packages/e2e-tests/test-applications/elysia-bun/tests/transactions.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ test('Creates lifecycle spans for Elysia hooks', async ({ baseURL, request }) =>
132132
expect(spans).toContainEqual(
133133
expect.objectContaining({
134134
description: 'Handle',
135-
op: 'request_handler.elysia',
135+
op: 'handler',
136136
origin: 'auto.http.elysia',
137137
}),
138138
);

‎dev-packages/e2e-tests/test-applications/elysia-node/tests/transactions.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ test('Creates lifecycle spans for Elysia hooks', async ({ baseURL, request }) =>
132132
expect(spans).toContainEqual(
133133
expect.objectContaining({
134134
description: 'Handle',
135-
op: 'request_handler.elysia',
135+
op: 'handler',
136136
origin: 'auto.http.elysia',
137137
}),
138138
);

‎dev-packages/e2e-tests/test-applications/hono-4/tests/multi-fetch.test.ts‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
185185
});
186186

187187
test.describe('trace propagation through internal .request() calls', () => {
188-
test('single internal fetch produces a hono.request child span', async ({ baseURL }) => {
188+
test('single internal fetch produces an internal-request child span', async ({ baseURL }) => {
189189
const transactionPromise = waitForTransaction(APP_NAME, event => {
190190
return (
191191
event.contexts?.trace?.op === 'http.server' && event.transaction === `GET ${STOREFRONT}/product/:productId`
@@ -198,20 +198,22 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
198198
const traceId = transaction.contexts?.trace?.trace_id;
199199
const spans = transaction.spans || [];
200200

201-
const internalRequestSpans = spans.filter((s: { op?: string }) => s.op === 'hono.request');
201+
const internalRequestSpans = spans.filter(
202+
(s: { origin?: string }) => s.origin === 'auto.http.hono.internal_request',
203+
);
202204

203205
expect(internalRequestSpans).toHaveLength(1);
204206
expect(internalRequestSpans[0]).toEqual(
205207
expect.objectContaining({
206-
op: 'hono.request',
208+
op: 'http.server',
207209
origin: 'auto.http.hono.internal_request',
208210
trace_id: traceId,
209211
}),
210212
);
211213
expect(internalRequestSpans[0]?.description).toContain('GET /item/self-watering-plant');
212214
});
213215

214-
test('parallel internal fetches produce two sibling hono.request spans', async ({ baseURL }) => {
216+
test('parallel internal fetches produce two sibling internal-request spans', async ({ baseURL }) => {
215217
const transactionPromise = waitForTransaction(APP_NAME, event => {
216218
return (
217219
event.contexts?.trace?.op === 'http.server' &&
@@ -225,7 +227,9 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
225227
const traceId = transaction.contexts?.trace?.trace_id;
226228
const spans = transaction.spans || [];
227229

228-
const internalRequestSpans = spans.filter((s: { op?: string }) => s.op === 'hono.request');
230+
const internalRequestSpans = spans.filter(
231+
(s: { origin?: string }) => s.origin === 'auto.http.hono.internal_request',
232+
);
229233

230234
expect(internalRequestSpans).toHaveLength(2);
231235

@@ -238,7 +242,7 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
238242
expect(internalRequestSpans[1]?.origin).toBe('auto.http.hono.internal_request');
239243
});
240244

241-
test('sequential chained fetches produce two ordered hono.request spans', async ({ baseURL }) => {
245+
test('sequential chained fetches produce two ordered internal-request spans', async ({ baseURL }) => {
242246
const transactionPromise = waitForTransaction(APP_NAME, event => {
243247
return (
244248
event.contexts?.trace?.op === 'http.server' &&
@@ -253,7 +257,7 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
253257
const spans = transaction.spans || [];
254258

255259
const internalRequestSpans = spans
256-
.filter((s: { op?: string }) => s.op === 'hono.request')
260+
.filter((s: { origin?: string }) => s.origin === 'auto.http.hono.internal_request')
257261
.sort(
258262
(a: { start_timestamp?: number }, b: { start_timestamp?: number }) =>
259263
(a.start_timestamp ?? 0) - (b.start_timestamp ?? 0),
@@ -270,7 +274,7 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
270274
expect(internalRequestSpans[1]?.trace_id).toBe(traceId);
271275
});
272276

273-
test('hono.request span has no error status for internal 4xx HTTPException', async ({ baseURL }) => {
277+
test('internal-request span has no error status for internal 4xx HTTPException', async ({ baseURL }) => {
274278
const transactionPromise = waitForTransaction(APP_NAME, event => {
275279
return (
276280
event.contexts?.trace?.op === 'http.server' &&
@@ -283,7 +287,9 @@ test.describe('multi-fetch: internal .request() calls between sub-apps', () => {
283287
const transaction = await transactionPromise;
284288
const spans = transaction.spans || [];
285289

286-
const internalRequestSpans = spans.filter((s: { op?: string }) => s.op === 'hono.request');
290+
const internalRequestSpans = spans.filter(
291+
(s: { origin?: string }) => s.origin === 'auto.http.hono.internal_request',
292+
);
287293

288294
expect(internalRequestSpans).toHaveLength(1);
289295
expect(internalRequestSpans[0]?.status).not.toBe('internal_error');

‎dev-packages/e2e-tests/test-applications/nestjs-11/tests/transactions.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ test('Sends an API route transaction', async ({ baseURL }) => {
6262
'express.type': 'request_handler',
6363
'http.route': '/test-transaction',
6464
'sentry.origin': 'auto.http.express',
65-
'sentry.op': 'request_handler.express',
65+
'sentry.op': 'handler',
6666
},
67-
op: 'request_handler.express',
67+
op: 'handler',
6868
description: '/test-transaction',
6969
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
7070
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -105,7 +105,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
105105
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
106106
data: {
107107
'sentry.origin': 'auto.http.nestjs',
108-
'sentry.op': 'handler.nestjs',
108+
'sentry.op': 'handler',
109109
component: '@nestjs/core',
110110
'nestjs.version': expect.any(String),
111111
'nestjs.type': 'handler',
@@ -117,7 +117,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
117117
timestamp: expect.any(Number),
118118
status: 'ok',
119119
origin: 'auto.http.nestjs',
120-
op: 'handler.nestjs',
120+
op: 'handler',
121121
},
122122
]),
123123
transaction: 'GET /test-transaction',

‎dev-packages/e2e-tests/test-applications/nestjs-8/tests/transactions.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ test('Sends an API route transaction', async ({ baseURL }) => {
6666
'express.type': 'request_handler',
6767
'http.route': '/test-transaction',
6868
'sentry.origin': 'auto.http.express',
69-
'sentry.op': 'request_handler.express',
69+
'sentry.op': 'handler',
7070
},
71-
op: 'request_handler.express',
71+
op: 'handler',
7272
description: '/test-transaction',
7373
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
7474
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -109,7 +109,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
109109
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
110110
data: {
111111
'sentry.origin': 'auto.http.nestjs',
112-
'sentry.op': 'handler.nestjs',
112+
'sentry.op': 'handler',
113113
component: '@nestjs/core',
114114
'nestjs.version': expect.any(String),
115115
'nestjs.type': 'handler',
@@ -121,7 +121,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
121121
timestamp: expect.any(Number),
122122
status: 'ok',
123123
origin: 'auto.http.nestjs',
124-
op: 'handler.nestjs',
124+
op: 'handler',
125125
},
126126
]),
127127
transaction: 'GET /test-transaction',

‎dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ test('Sends an API route transaction', async ({ baseURL }) => {
8888
'express.type': 'request_handler',
8989
'http.route': '/test-transaction',
9090
'sentry.origin': 'auto.http.express',
91-
'sentry.op': 'request_handler.express',
91+
'sentry.op': 'handler',
9292
},
93-
op: 'request_handler.express',
93+
op: 'handler',
9494
description: '/test-transaction',
9595
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
9696
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -131,7 +131,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
131131
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
132132
data: {
133133
'sentry.origin': 'auto.http.nestjs',
134-
'sentry.op': 'handler.nestjs',
134+
'sentry.op': 'handler',
135135
component: '@nestjs/core',
136136
'nestjs.version': expect.any(String),
137137
'nestjs.type': 'handler',
@@ -143,7 +143,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
143143
timestamp: expect.any(Number),
144144
status: 'ok',
145145
origin: 'auto.http.nestjs',
146-
op: 'handler.nestjs',
146+
op: 'handler',
147147
},
148148
]),
149149
transaction: 'GET /test-transaction',

‎dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => {
8383
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
8484
data: {
8585
'sentry.origin': 'auto.http.otel.fastify',
86-
'sentry.op': 'request_handler.fastify',
86+
'sentry.op': 'handler',
8787
'hook.name': 'fastify -> @sentry/instrumentation-fastify -> @fastify/middie - route-handler',
8888
'fastify.type': 'request-handler',
8989
'http.route': '/test-transaction',
9090
'hook.callback.name': 'anonymous',
9191
},
9292
description: '@fastify/middie - route-handler',
93-
op: 'request_handler.fastify',
93+
op: 'handler',
9494
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
9595
start_timestamp: expect.any(Number),
9696
timestamp: expect.any(Number),
@@ -125,7 +125,7 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => {
125125
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
126126
data: {
127127
'sentry.origin': 'auto.http.nestjs',
128-
'sentry.op': 'handler.nestjs',
128+
'sentry.op': 'handler',
129129
component: '@nestjs/core',
130130
'nestjs.version': expect.any(String),
131131
'nestjs.type': 'handler',
@@ -136,7 +136,7 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => {
136136
start_timestamp: expect.any(Number),
137137
timestamp: expect.any(Number),
138138
status: 'ok',
139-
op: 'handler.nestjs',
139+
op: 'handler',
140140
origin: 'auto.http.nestjs',
141141
},
142142
{
@@ -799,7 +799,7 @@ test('Sets error status on nest spans when a handler throws', async ({ baseURL }
799799
expect(transactionEvent.spans).toEqual(
800800
expect.arrayContaining([
801801
expect.objectContaining({ op: 'request_context.nestjs', status: 'internal_error' }),
802-
expect.objectContaining({ op: 'handler.nestjs', status: 'internal_error' }),
802+
expect.objectContaining({ op: 'handler', status: 'internal_error' }),
803803
]),
804804
);
805805
});

‎dev-packages/e2e-tests/test-applications/nestjs-orchestrion/tests/transactions.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ test('request_context + handler: a route transaction nests the nestjs spans', as
6060

6161
// request_handler span: wraps the controller method itself.
6262
const handler = (transactionEvent.spans ?? []).find(
63-
span => span.op === 'handler.nestjs' && span.description === 'testTransaction',
63+
span => span.op === 'handler' && span.description === 'testTransaction',
6464
);
6565
expect(handler).toBeDefined();
6666
});

‎dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
6262
'express.type': 'request_handler',
6363
'http.route': '/example-module/transaction',
6464
'sentry.origin': 'auto.http.express',
65-
'sentry.op': 'request_handler.express',
65+
'sentry.op': 'handler',
6666
},
67-
op: 'request_handler.express',
67+
op: 'handler',
6868
description: '/example-module/transaction',
6969
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
7070
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -105,7 +105,7 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
105105
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
106106
data: {
107107
'sentry.origin': 'auto.http.nestjs',
108-
'sentry.op': 'handler.nestjs',
108+
'sentry.op': 'handler',
109109
component: '@nestjs/core',
110110
'nestjs.version': expect.any(String),
111111
'nestjs.type': 'handler',
@@ -117,7 +117,7 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
117117
timestamp: expect.any(Number),
118118
status: 'ok',
119119
origin: 'auto.http.nestjs',
120-
op: 'handler.nestjs',
120+
op: 'handler',
121121
},
122122
]),
123123
transaction: 'GET /example-module/transaction',

‎dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
6262
'express.type': 'request_handler',
6363
'http.route': '/example-module/transaction',
6464
'sentry.origin': 'auto.http.express',
65-
'sentry.op': 'request_handler.express',
65+
'sentry.op': 'handler',
6666
},
67-
op: 'request_handler.express',
67+
op: 'handler',
6868
description: '/example-module/transaction',
6969
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
7070
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -105,7 +105,7 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
105105
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
106106
data: {
107107
'sentry.origin': 'auto.http.nestjs',
108-
'sentry.op': 'handler.nestjs',
108+
'sentry.op': 'handler',
109109
component: '@nestjs/core',
110110
'nestjs.version': expect.any(String),
111111
'nestjs.type': 'handler',
@@ -117,7 +117,7 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
117117
timestamp: expect.any(Number),
118118
status: 'ok',
119119
origin: 'auto.http.nestjs',
120-
op: 'handler.nestjs',
120+
op: 'handler',
121121
},
122122
]),
123123
transaction: 'GET /example-module/transaction',

0 commit comments

Comments
 (0)