Skip to content

Commit dbe044c

Browse files
Lms24claude
andcommitted
feat(core): Deprecate op span start option
`op` duplicates the `sentry.op` span attribute, which is the canonical way to categorize a span. It will be removed in a future version; the deprecation deliberately does not name a specific one. All internal usages are rewritten to set `sentry.op` (via the `SENTRY_OP` constant from `@sentry/conventions`) instead. Precedence is unchanged everywhere: an explicit `sentry.op` attribute still wins over `op`. Call sites that already set both now only set the attribute. `browserTracingIntegration` is the one exception that still touches `op`: `beforeStartSpan` is a public hook that receives and may override it, so the option is kept in the options handed to the hook and only folded into `sentry.op` afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d6d11fe commit dbe044c

25 files changed

Lines changed: 98 additions & 55 deletions

File tree

‎packages/astro/src/server/middleware.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const handleRequest: (options?: MiddlewareOptions) => MiddlewareHandler =
108108
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
109109

110110
// if there is an active span, we just want to enhance it with routing data etc.
111-
if (rootSpan && spanToJSON(rootSpan).attributes[SENTRY_OP] === 'http.server') {
111+
if (rootSpan && spanToJSON(rootSpan).attributes[SENTRY_OP] === HTTP_SERVER) {
112112
return enhanceHttpServerSpan(ctx, next, rootSpan);
113113
}
114114

@@ -252,7 +252,10 @@ async function instrumentRequestStartHttpServerSpan(
252252

253253
const res = await startSpan(
254254
{
255-
attributes,
255+
attributes: {
256+
[SENTRY_OP]: HTTP_SERVER,
257+
...attributes,
258+
},
256259
name,
257260
},
258261
async span => {

‎packages/browser-utils/src/performance/entries.ts‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ export function startTrackingLongTasks(): void {
118118

119119
startAndEndSpan(parent, startTime, startTime + duration, {
120120
name: 'Main UI thread blocked',
121-
op: UI_LONG_TASK,
122121
attributes: {
122+
[SENTRY_OP]: UI_LONG_TASK,
123123
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
124124
},
125125
});
@@ -161,6 +161,7 @@ export function startTrackingLongAnimationFrames(): void {
161161
const duration = msToSec(entry.duration);
162162

163163
const attributes: SpanAttributes = {
164+
[SENTRY_OP]: UI_LONG_ANIMATION_FRAME,
164165
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',
165166
};
166167

@@ -180,7 +181,6 @@ export function startTrackingLongAnimationFrames(): void {
180181

181182
startAndEndSpan(parent, startTime, startTime + duration, {
182183
name: 'Main UI thread blocked',
183-
op: UI_LONG_ANIMATION_FRAME,
184184
attributes,
185185
});
186186
}
@@ -464,7 +464,11 @@ export function _addResourceSpans(
464464
['deliveryType', 'http.response_delivery_type'],
465465
]);
466466

467-
const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) };
467+
const attributesWithResourceTiming: SpanAttributes = {
468+
[SENTRY_OP]: op,
469+
...attributes,
470+
...resourceTimingToSpanAttributes(entry),
471+
};
468472

469473
const startTimestamp = timeOrigin + startTime;
470474
const endTimestamp = startTimestamp + duration;
@@ -474,7 +478,6 @@ export function _addResourceSpans(
474478
name: spanStreamingEnabled
475479
? domain || RESOURCE_SPAN_NAME_FALLBACK
476480
: resourceUrl.replace(WINDOW.location.origin, ''),
477-
op,
478481
attributes: attributesWithResourceTiming,
479482
});
480483
}

‎packages/browser-utils/src/performance/userTiming.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export function _addUserTimingSpan(
116116
const spanEndTimestamp = originalStartTimestamp + duration;
117117

118118
const attributes: SpanAttributes = {
119+
[SENTRY_OP]: entry.entryType,
119120
[SENTRY_ORIGIN]: `auto.browser.user_timing.${entry.entryType}`,
120121
};
121122

@@ -130,7 +131,6 @@ export function _addUserTimingSpan(
130131
if (spanStartTimestamp <= spanEndTimestamp) {
131132
startAndEndSpan(parentSpan, spanStartTimestamp, spanEndTimestamp, {
132133
name: entry.name,
133-
op: entry.entryType,
134134
attributes,
135135
});
136136
}

‎packages/browser/src/tracing/browserTracingIntegration.ts‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,12 +331,14 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
331331

332332
/** Create routing idle transaction. */
333333
function _createRouteSpan(client: Client, startSpanOptions: StartSpanOptions, makeActive = true, url?: string): void {
334-
const isPageloadSpan = startSpanOptions.op === 'pageload';
334+
const originalOp = startSpanOptions.attributes?.[SENTRY_OP];
335+
// backfill top-level `op` option
336+
// oxlint-disable-next-line typescript/no-deprecated
337+
const optionsWithOp: StartSpanOptions = { op: originalOp, ...startSpanOptions };
338+
const isPageloadSpan = originalOp === PAGELOAD;
335339

336-
const initialSpanName = startSpanOptions.name;
337-
const finalStartSpanOptions: StartSpanOptions = beforeStartSpan
338-
? beforeStartSpan(startSpanOptions)
339-
: startSpanOptions;
340+
const initialSpanName = optionsWithOp.name;
341+
const finalStartSpanOptions: StartSpanOptions = beforeStartSpan ? beforeStartSpan(optionsWithOp) : optionsWithOp;
340342

341343
// For navigations, `url` is the destination URL, so we use it to reflect the post-navigation location.
342344
// For pageloads (and manual navigation spans without a URL) we fall back to the current location.
@@ -348,6 +350,12 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
348350
...finalStartSpanOptions.attributes,
349351
};
350352

353+
// oxlint-disable-next-line typescript/no-deprecated
354+
if (finalStartSpanOptions.op !== originalOp) {
355+
// oxlint-disable-next-line typescript/no-deprecated
356+
attributes[SENTRY_OP] = finalStartSpanOptions.op;
357+
}
358+
351359
// If `finalStartSpanOptions.name` is different than `startSpanOptions.name`
352360
// it is because `beforeStartSpan` set a custom name. Therefore we set the source to 'custom'.
353361
if (initialSpanName !== finalStartSpanOptions.name) {
@@ -485,8 +493,8 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
485493
_createRouteSpan(
486494
client,
487495
{
488-
op: NAVIGATION_REDIRECT,
489496
...startSpanOptions,
497+
attributes: { [SENTRY_OP]: NAVIGATION_REDIRECT, ...startSpanOptions.attributes },
490498
},
491499
false,
492500
navigationOptions.url,
@@ -517,8 +525,8 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
517525
_createRouteSpan(
518526
client,
519527
{
520-
op: NAVIGATION,
521528
...startSpanOptions,
529+
attributes: { [SENTRY_OP]: NAVIGATION, ...startSpanOptions.attributes },
522530
// Navigation starts a new trace and is NOT parented under any active interaction (e.g. ui.action.click)
523531
parentSpan: null,
524532
},
@@ -555,8 +563,8 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
555563
});
556564

557565
_createRouteSpan(client, {
558-
op: PAGELOAD,
559566
...startSpanOptions,
567+
attributes: { [SENTRY_OP]: PAGELOAD, ...startSpanOptions.attributes },
560568
});
561569
});
562570

‎packages/cloudflare/src/instrumentations/instrumentDurableObjectSyncKvStorage.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { SyncKvStorage } from '@cloudflare/workers-types';
21
import { SENTRY_OP } from '@sentry/conventions/attributes';
2+
import type { SyncKvStorage } from '@cloudflare/workers-types';
33
import { DB } from '@sentry/conventions/op';
44
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';
55

‎packages/cloudflare/src/instrumentations/instrumentSqlStorage.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { SqlStorage } from '@cloudflare/workers-types';
21
import { SENTRY_OP } from '@sentry/conventions/attributes';
2+
import type { SqlStorage } from '@cloudflare/workers-types';
33
import { DB_QUERY } from '@sentry/conventions/op';
44
import {
55
_INTERNAL_getSqlQuerySummary,

‎packages/cloudflare/src/instrumentations/worker/instrumentR2.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ function createSpanOptions(bindingName: string, r2Op: R2OperationKey, key?: stri
5858
const requestKey = Array.isArray(key) ? key.join(', ') : typeof key === 'string' ? key : undefined;
5959

6060
return {
61-
op,
6261
name: spanName,
6362
attributes: {
6463
[CLOUDFLARE_R2_OPERATION]: operation,

‎packages/cloudflare/test/instrumentations/worker/instrumentR2.test.ts‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ describe('instrumentR2Bucket', () => {
8080
expect(startSpanSpy).toHaveBeenCalledTimes(1);
8181
expect(startSpanSpy).toHaveBeenLastCalledWith(
8282
expect.objectContaining({
83-
op: 'object.get',
8483
name: 'r2_get',
8584
attributes: expect.objectContaining({
8685
'cloudflare.r2.operation': 'GetObject',
@@ -112,7 +111,6 @@ describe('instrumentR2Bucket', () => {
112111
expect(startSpanSpy).toHaveBeenCalledTimes(1);
113112
expect(startSpanSpy).toHaveBeenLastCalledWith(
114113
expect.objectContaining({
115-
op: 'object.head',
116114
name: 'r2_head',
117115
attributes: expect.objectContaining({
118116
'cloudflare.r2.operation': 'HeadObject',
@@ -142,7 +140,6 @@ describe('instrumentR2Bucket', () => {
142140
expect(startSpanSpy).toHaveBeenCalledTimes(1);
143141
expect(startSpanSpy).toHaveBeenLastCalledWith(
144142
expect.objectContaining({
145-
op: 'object.put',
146143
name: 'r2_put',
147144
attributes: expect.objectContaining({
148145
'cloudflare.r2.operation': 'PutObject',
@@ -170,7 +167,6 @@ describe('instrumentR2Bucket', () => {
170167
expect(startSpanSpy).toHaveBeenCalledTimes(1);
171168
expect(startSpanSpy).toHaveBeenLastCalledWith(
172169
expect.objectContaining({
173-
op: 'object.delete',
174170
name: 'r2_delete',
175171
attributes: expect.objectContaining({
176172
'cloudflare.r2.operation': 'DeleteObject',
@@ -206,7 +202,6 @@ describe('instrumentR2Bucket', () => {
206202
expect(startSpanSpy).toHaveBeenCalledTimes(1);
207203
expect(startSpanSpy).toHaveBeenLastCalledWith(
208204
expect.objectContaining({
209-
op: 'object.list',
210205
name: 'r2_list',
211206
attributes: expect.objectContaining({
212207
'cloudflare.r2.operation': 'ListObjects',
@@ -236,11 +231,11 @@ describe('instrumentR2Bucket', () => {
236231
expect(startSpanSpy).toHaveBeenCalledTimes(1);
237232
expect(startSpanSpy).toHaveBeenLastCalledWith(
238233
expect.objectContaining({
239-
op: 'object.multipart_upload.create',
240234
name: 'r2_createMultipartUpload',
241235
attributes: expect.objectContaining({
242236
'cloudflare.r2.operation': 'CreateMultipartUpload',
243237
'cloudflare.r2.request.key': 'big-file.bin',
238+
'sentry.op': 'object.multipart_upload.create',
244239
}),
245240
}),
246241
expect.any(Function),

‎packages/core/src/integrations/mcp-server/spans.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export function buildMcpServerSpanConfig(
212212

213213
return {
214214
name: spanName,
215+
// oxlint-disable-next-line typescript/no-deprecated
215216
forceTransaction: true,
216217
attributes,
217218
};

‎packages/core/src/tracing/trace.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,10 @@ function parseSentrySpanArguments(options: StartSpanOptions): SentrySpanArgument
445445

446446
// Fold `op` into the attributes up front so samplers see `sentry.op`; the `SentrySpan`
447447
// constructor only adds it after the sampling decision. An explicit `sentry.op` attribute wins.
448+
// oxlint-disable-next-line typescript/no-deprecated
448449
if (options.op) {
449450
initialCtx.attributes = {
451+
// oxlint-disable-next-line typescript/no-deprecated
450452
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: options.op,
451453
...options.attributes,
452454
};

0 commit comments

Comments
 (0)