Skip to content

Commit fb7e516

Browse files
authored
feat(core): Deprecate Span.op in favor of op attribute (#10189)
Deprecate `Span.op` in favour of: - `spanToJson` to get the span op - `startSpan` functions to set the initial span op - `setAttribute('SEMANTIC_ATTRIBUTE_SENTRY_OP', ...)` to update the span op Going forward, the span op will become a semantic, Sentry-specific span attribute. With this PR we already set, get and update the attribute but still provide the deprecated `op` getter and setter for v7 backwards compatibility.
1 parent 64ba9ec commit fb7e516

File tree

20 files changed

+101
-28
lines changed

20 files changed

+101
-28
lines changed

MIGRATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ In v8, the Span class is heavily reworked. The following properties & methods ar
205205
- `span.transaction`: Use `getRootSpan` utility function instead.
206206
- `span.spanRecorder`: Span recording will be handled internally by the SDK.
207207
- `span.status`: Use `.setStatus` to set or update and `spanToJSON()` to read the span status.
208+
- `span.op`: Use `startSpan` functions to set, `setAttribute()` to update and `spanToJSON` to read the span operation.
208209
- `transaction.setMetadata()`: Use attributes instead, or set data on the scope.
209210
- `transaction.metadata`: Use attributes instead, or set data on the scope.
210211
- `transaction.setContext()`: Set context on the surrounding scope instead.

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ test('Should trace outgoing fetch requests inside middleware and create breadcru
6363
expect(middlewareTransaction.spans).toEqual(
6464
expect.arrayContaining([
6565
{
66-
data: { 'http.method': 'GET', 'http.response.status_code': 200, type: 'fetch', url: 'http://localhost:3030/' },
66+
data: {
67+
'http.method': 'GET',
68+
'http.response.status_code': 200,
69+
type: 'fetch',
70+
url: 'http://localhost:3030/',
71+
'sentry.op': 'http.client',
72+
},
6773
description: 'GET http://localhost:3030/',
6874
op: 'http.client',
6975
origin: 'auto.http.wintercg_fetch',

dev-packages/e2e-tests/test-applications/node-experimental-fastify-app/tests/propagation.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ test('Propagates trace for outgoing http requests', async ({ baseURL }) => {
6363
url: 'http://localhost:3030/test-outgoing-http',
6464
'otel.kind': 'SERVER',
6565
'http.response.status_code': 200,
66+
'sentry.op': 'http.server',
6667
},
6768
op: 'http.server',
6869
span_id: expect.any(String),
@@ -85,6 +86,7 @@ test('Propagates trace for outgoing http requests', async ({ baseURL }) => {
8586
url: 'http://localhost:3030/test-inbound-headers',
8687
'otel.kind': 'SERVER',
8788
'http.response.status_code': 200,
89+
'sentry.op': 'http.server',
8890
},
8991
op: 'http.server',
9092
parent_span_id: outgoingHttpSpanId,
@@ -156,6 +158,7 @@ test('Propagates trace for outgoing fetch requests', async ({ baseURL }) => {
156158
url: 'http://localhost:3030/test-outgoing-fetch',
157159
'otel.kind': 'SERVER',
158160
'http.response.status_code': 200,
161+
'sentry.op': 'http.server',
159162
},
160163
op: 'http.server',
161164
span_id: expect.any(String),
@@ -178,6 +181,7 @@ test('Propagates trace for outgoing fetch requests', async ({ baseURL }) => {
178181
url: 'http://localhost:3030/test-inbound-headers',
179182
'otel.kind': 'SERVER',
180183
'http.response.status_code': 200,
184+
'sentry.op': 'http.server',
181185
},
182186
op: 'http.server',
183187
parent_span_id: outgoingHttpSpanId,

dev-packages/e2e-tests/test-applications/node-experimental-fastify-app/tests/transactions.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
2828
url: 'http://localhost:3030/test-transaction',
2929
'otel.kind': 'SERVER',
3030
'http.response.status_code': 200,
31+
'sentry.op': 'http.server',
3132
},
3233
op: 'http.server',
3334
span_id: expect.any(String),

packages/core/src/semanticAttributes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ export const SEMANTIC_ATTRIBUTE_SENTRY_SOURCE = 'sentry.source';
99
* Use this attribute to represent the sample rate used for a span.
1010
*/
1111
export const SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE = 'sentry.sample_rate';
12+
13+
/**
14+
* Use this attribute to represent the operation of a span.
15+
*/
16+
export const SEMANTIC_ATTRIBUTE_SENTRY_OP = 'sentry.op';

packages/core/src/tracing/idletransaction.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@ export class IdleTransaction extends Transaction {
148148
this._finished = true;
149149
this.activities = {};
150150

151+
// eslint-disable-next-line deprecation/deprecation
151152
if (this.op === 'ui.action.click') {
152153
this.setAttribute(FINISH_REASON_TAG, this._finishReason);
153154
}
154155

155156
// eslint-disable-next-line deprecation/deprecation
156157
if (this.spanRecorder) {
157158
DEBUG_BUILD &&
159+
// eslint-disable-next-line deprecation/deprecation
158160
logger.log('[Tracing] finishing IdleTransaction', new Date(endTimestampInS * 1000).toISOString(), this.op);
159161

160162
for (const callback of this._beforeFinishCallbacks) {

packages/core/src/tracing/sampling.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function sampleTransaction<T extends Transaction>(
9595
}
9696

9797
DEBUG_BUILD &&
98+
// eslint-disable-next-line deprecation/deprecation
9899
logger.log(`[Tracing] starting ${transaction.op} transaction - ${spanToJSON(transaction).description}`);
99100
return transaction;
100101
}

packages/core/src/tracing/span.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/utils';
1717

1818
import { DEBUG_BUILD } from '../debug-build';
19+
import { SEMANTIC_ATTRIBUTE_SENTRY_OP } from '../semanticAttributes';
1920
import { getRootSpan } from '../utils/getRootSpan';
2021
import {
2122
TRACE_FLAG_NONE,
@@ -67,11 +68,6 @@ export class Span implements SpanInterface {
6768
*/
6869
public parentSpanId?: string;
6970

70-
/**
71-
* @inheritDoc
72-
*/
73-
public op?: string;
74-
7571
/**
7672
* Tags for the span.
7773
* @deprecated Use `getSpanAttributes(span)` instead.
@@ -158,7 +154,7 @@ export class Span implements SpanInterface {
158154
this._sampled = spanContext.sampled;
159155
}
160156
if (spanContext.op) {
161-
this.op = spanContext.op;
157+
this.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, spanContext.op);
162158
}
163159
if (spanContext.status) {
164160
this._status = spanContext.status;
@@ -317,6 +313,25 @@ export class Span implements SpanInterface {
317313
this._status = status;
318314
}
319315

316+
/**
317+
* Operation of the span
318+
*
319+
* @deprecated Use `spanToJSON().op` to read the op instead.
320+
*/
321+
public get op(): string | undefined {
322+
return this._attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] as string | undefined;
323+
}
324+
325+
/**
326+
* Operation of the span
327+
*
328+
* @deprecated Use `startSpan()` functions to set or `span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'op')
329+
* to update the span instead.
330+
*/
331+
public set op(op: string | undefined) {
332+
this.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, op);
333+
}
334+
320335
/* eslint-enable @typescript-eslint/member-ordering */
321336

322337
/** @inheritdoc */
@@ -512,6 +527,7 @@ export class Span implements SpanInterface {
512527
data: this._getData(),
513528
description: this._name,
514529
endTimestamp: this._endTime,
530+
// eslint-disable-next-line deprecation/deprecation
515531
op: this.op,
516532
parentSpanId: this.parentSpanId,
517533
sampled: this._sampled,
@@ -535,6 +551,7 @@ export class Span implements SpanInterface {
535551
// eslint-disable-next-line deprecation/deprecation
536552
this._name = spanContext.name || spanContext.description;
537553
this._endTime = spanContext.endTimestamp;
554+
// eslint-disable-next-line deprecation/deprecation
538555
this.op = spanContext.op;
539556
this.parentSpanId = spanContext.parentSpanId;
540557
this._sampled = spanContext.sampled;
@@ -569,7 +586,7 @@ export class Span implements SpanInterface {
569586
return dropUndefinedKeys({
570587
data: this._getData(),
571588
description: this._name,
572-
op: this.op,
589+
op: this._attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] as string | undefined,
573590
parent_span_id: this.parentSpanId,
574591
span_id: this._spanId,
575592
start_timestamp: this._startTime,

packages/core/src/tracing/transaction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ export class Transaction extends SpanClass implements TransactionInterface {
344344
transaction.measurements = this._measurements;
345345
}
346346

347+
// eslint-disable-next-line deprecation/deprecation
347348
DEBUG_BUILD && logger.log(`[Tracing] Finishing ${this.op} transaction: ${this._name}.`);
348349

349350
return transaction;

packages/core/test/lib/utils/spanUtils.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ describe('spanToJSON', () => {
8787
origin: 'auto',
8888
start_timestamp: 123,
8989
timestamp: 456,
90+
data: {
91+
'sentry.op': 'test op',
92+
},
9093
});
9194
});
9295

packages/nextjs/test/integration/test/client/tracingFetch.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test('should correctly instrument `fetch` for performance tracing', async ({ pag
3737
type: 'fetch',
3838
'http.response_content_length': expect.any(Number),
3939
'http.response.status_code': 200,
40+
'sentry.op': 'http.client',
4041
},
4142
description: 'GET http://example.com',
4243
op: 'http.client',

packages/node-experimental/test/integration/transactions.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('Integration | Transactions', () => {
8484
},
8585
runtime: { name: 'node', version: expect.any(String) },
8686
trace: {
87-
data: { 'otel.kind': 'INTERNAL' },
87+
data: { 'otel.kind': 'INTERNAL', 'sentry.op': 'test op' },
8888
op: 'test op',
8989
span_id: expect.any(String),
9090
status: 'ok',
@@ -244,7 +244,7 @@ describe('Integration | Transactions', () => {
244244
},
245245
}),
246246
trace: {
247-
data: { 'otel.kind': 'INTERNAL' },
247+
data: { 'otel.kind': 'INTERNAL', 'sentry.op': 'test op' },
248248
op: 'test op',
249249
span_id: expect.any(String),
250250
status: 'ok',
@@ -286,7 +286,7 @@ describe('Integration | Transactions', () => {
286286
},
287287
}),
288288
trace: {
289-
data: { 'otel.kind': 'INTERNAL' },
289+
data: { 'otel.kind': 'INTERNAL', 'sentry.op': 'test op b' },
290290
op: 'test op b',
291291
span_id: expect.any(String),
292292
status: 'ok',
@@ -363,7 +363,7 @@ describe('Integration | Transactions', () => {
363363
attributes: {},
364364
}),
365365
trace: {
366-
data: { 'otel.kind': 'INTERNAL' },
366+
data: { 'otel.kind': 'INTERNAL', 'sentry.op': 'test op' },
367367
op: 'test op',
368368
span_id: expect.any(String),
369369
parent_span_id: parentSpanId,

packages/node/test/integrations/http.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ describe('tracing', () => {
7979

8080
// our span is at index 1 because the transaction itself is at index 0
8181
expect(spanToJSON(spans[1]).description).toEqual('GET http://dogs.are.great/');
82+
// eslint-disable-next-line deprecation/deprecation
8283
expect(spans[1].op).toEqual('http.client');
84+
expect(spanToJSON(spans[1]).op).toEqual('http.client');
8385
});
8486

8587
it("doesn't create a span for outgoing sentry requests", () => {
@@ -283,7 +285,9 @@ describe('tracing', () => {
283285

284286
// our span is at index 1 because the transaction itself is at index 0
285287
expect(spanToJSON(spans[1]).description).toEqual('GET http://dogs.are.great/spaniel');
288+
// eslint-disable-next-line deprecation/deprecation
286289
expect(spans[1].op).toEqual('http.client');
290+
expect(spanToJSON(spans[1]).op).toEqual('http.client');
287291

288292
const spanAttributes = spanToJSON(spans[1]).data || {};
289293

@@ -308,7 +312,9 @@ describe('tracing', () => {
308312

309313
// our span is at index 1 because the transaction itself is at index 0
310314
expect(spanToJSON(spans[1]).description).toEqual('GET http://dogs.are.great/spaniel');
315+
// eslint-disable-next-line deprecation/deprecation
311316
expect(spans[1].op).toEqual('http.client');
317+
expect(spanToJSON(spans[1]).op).toEqual('http.client');
312318
expect(spanAttributes['http.method']).toEqual('GET');
313319
expect(spanAttributes.url).toEqual('http://dogs.are.great/spaniel');
314320
expect(spanAttributes['http.query']).toEqual('tail=wag&cute=true');
@@ -391,6 +397,7 @@ describe('tracing', () => {
391397
const request = http.get(url);
392398

393399
// There should be no http spans
400+
// eslint-disable-next-line deprecation/deprecation
394401
const httpSpans = spans.filter(span => span.op?.startsWith('http'));
395402
expect(httpSpans.length).toBe(0);
396403

@@ -497,6 +504,7 @@ describe('tracing', () => {
497504
const request = http.get(url);
498505

499506
// There should be no http spans
507+
// eslint-disable-next-line deprecation/deprecation
500508
const httpSpans = spans.filter(span => span.op?.startsWith('http'));
501509
expect(httpSpans.length).toBe(0);
502510

packages/opentelemetry-node/src/spanprocessor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SpanKind, context, trace } from '@opentelemetry/api';
33
import { suppressTracing } from '@opentelemetry/core';
44
import type { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base';
55
import {
6+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
67
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
78
Transaction,
89
addEventProcessor,
@@ -221,7 +222,7 @@ function updateTransactionWithOtelData(transaction: Transaction, otelSpan: OtelS
221222

222223
transaction.setStatus(mapOtelStatus(otelSpan));
223224

224-
transaction.op = op;
225+
transaction.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, op);
225226
transaction.updateName(description);
226227
transaction.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
227228
}

packages/opentelemetry-node/test/spanprocessor.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ describe('SentrySpanProcessor', () => {
560560
'http.url': 'http://example.com/my/route/123',
561561
'otel.kind': 'INTERNAL',
562562
url: 'http://example.com/my/route/123',
563+
'sentry.op': 'http',
563564
});
564565

565566
parentOtelSpan.end();
@@ -580,7 +581,7 @@ describe('SentrySpanProcessor', () => {
580581

581582
child.end();
582583

583-
const { description, data } = spanToJSON(sentrySpan!);
584+
const { description, data, op } = spanToJSON(sentrySpan!);
584585

585586
expect(description).toBe('GET http://example.com/my/route/123');
586587
expect(data).toEqual({
@@ -589,7 +590,9 @@ describe('SentrySpanProcessor', () => {
589590
'http.url': 'http://example.com/my/route/123',
590591
'otel.kind': 'INTERNAL',
591592
url: 'http://example.com/my/route/123',
593+
'sentry.op': 'http',
592594
});
595+
expect(op).toBe('http');
593596

594597
parentOtelSpan.end();
595598
});
@@ -609,7 +612,7 @@ describe('SentrySpanProcessor', () => {
609612

610613
child.end();
611614

612-
const { description, data } = spanToJSON(sentrySpan!);
615+
const { description, data, op } = spanToJSON(sentrySpan!);
613616

614617
expect(description).toBe('GET http://example.com/my/route/123');
615618
expect(data).toEqual({
@@ -620,7 +623,9 @@ describe('SentrySpanProcessor', () => {
620623
url: 'http://example.com/my/route/123',
621624
'http.query': '?what=123',
622625
'http.fragment': '#myHash',
626+
'sentry.op': 'http',
623627
});
628+
expect(op).toBe('http');
624629

625630
parentOtelSpan.end();
626631
});
@@ -780,7 +785,10 @@ describe('SentrySpanProcessor', () => {
780785
parentOtelSpan.setAttribute(SemanticAttributes.FAAS_TRIGGER, 'test faas trigger');
781786
parentOtelSpan.end();
782787

788+
// eslint-disable-next-line deprecation/deprecation
783789
expect(transaction.op).toBe('test faas trigger');
790+
expect(spanToJSON(transaction).op).toBe('test faas trigger');
791+
784792
expect(spanToJSON(transaction).description).toBe('test operation');
785793
});
786794
});

0 commit comments

Comments
 (0)