Skip to content

Commit 5625789

Browse files
committed
feat(core): Deprecate span tags, data, context & setters
Also deprecate direct access to `span.attributes` (instead use `spanGetAttributes(span)`). There are a few usages that we still need to figure out how we will replace them...!
1 parent 59ffba3 commit 5625789

File tree

37 files changed

+291
-157
lines changed

37 files changed

+291
-157
lines changed

MIGRATION.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ In v8, the Span class is heavily reworked. The following properties & methods ar
1717
* `span.setName(newName)`: Use `span.updateName(newName)` instead.
1818
* `span.toTraceparent()`: use `spanToTraceHeader(span)` util instead.
1919
* `span.getTraceContext()`: Use `spanToTraceContext(span)` utility function instead.
20+
* `span.tags`: Set tags on the surrounding scope instead, or use attributes.
21+
* `span.data`: Use `spanGetAttributes(span)` instead.
22+
* `span.setTag()`: Use `span.setAttribute()` instead or set tags on the surrounding scope.
23+
* `span.setData()`: Use `span.setAttribute()` instead.
24+
* `transaction.setContext()`: Set context on the surrounding scope instead.
2025

2126
## Deprecate `pushScope` & `popScope` in favor of `withScope`
2227

dev-packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ sentryTest('should report finished spans as children of the root transaction', a
3131
const span_1 = transaction.spans?.[0];
3232
expect(span_1?.op).toBe('span_1');
3333
expect(span_1?.parentSpanId).toEqual(rootSpanId);
34+
// eslint-disable-next-line deprecation/deprecation
3435
expect(span_1?.data).toMatchObject({ foo: 'bar', baz: [1, 2, 3] });
3536

3637
const span_3 = transaction.spans?.[1];

dev-packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/withoutCallback/scenario.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ const query = connection.query('SELECT 1 + 1 AS solution');
3030
const query2 = connection.query('SELECT NOW()', ['1', '2']);
3131

3232
query.on('end', () => {
33-
transaction.setTag('result_done', 'yes');
33+
transaction.setAttribute('result_done', 'yes');
3434

3535
query2.on('end', () => {
36-
transaction.setTag('result_done2', 'yes');
36+
transaction.setAttribute('result_done2', 'yes');
3737

3838
// Wait a bit to ensure the queries completed
3939
setTimeout(() => {

dev-packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/withoutCallback/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ test('should auto-instrument `mysql` package when using query without callback',
88

99
assertSentryTransaction(envelope[2], {
1010
transaction: 'Test Transaction',
11-
tags: {
11+
data: {
1212
result_done: 'yes',
1313
result_done2: 'yes',
1414
},

packages/browser/src/profiling/hubextensions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ export function startProfileForTransaction(transaction: Transaction): Transactio
159159
// Always call onProfileHandler to ensure stopProfiling is called and the timeout is cleared.
160160
void onProfileHandler().then(
161161
() => {
162+
// TODO: Can we rewrite this to use attributes?
163+
// eslint-disable-next-line deprecation/deprecation
162164
transaction.setContext('profile', { profile_id: profileId, start_timestamp: startTimestamp });
163165
originalEnd();
164166
},

packages/bun/src/integrations/bunserver.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
captureException,
44
continueTrace,
55
convertIntegrationFnToClass,
6+
getCurrentScope,
67
runWithAsyncContext,
78
startSpan,
89
} from '@sentry/core';
@@ -88,9 +89,10 @@ function instrumentBunServeOptions(serveOptions: Parameters<typeof Bun.serve>[0]
8889
>);
8990
if (response && response.status) {
9091
span?.setHttpStatus(response.status);
91-
span?.setData('http.response.status_code', response.status);
92+
span?.setAttribute('http.response.status_code', response.status);
9293
if (span instanceof Transaction) {
93-
span.setContext('response', {
94+
const scope = getCurrentScope();
95+
scope.setContext('response', {
9496
headers: response.headers.toJSON(),
9597
status_code: response.status,
9698
});

packages/bun/test/integrations/bunserver.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('Bun Serve Integration', () => {
2626
test('generates a transaction around a request', async () => {
2727
client.on('finishTransaction', transaction => {
2828
expect(transaction.status).toBe('ok');
29+
// eslint-disable-next-line deprecation/deprecation
2930
expect(transaction.tags).toEqual({
3031
'http.status_code': '200',
3132
});
@@ -48,6 +49,7 @@ describe('Bun Serve Integration', () => {
4849
test('generates a post transaction', async () => {
4950
client.on('finishTransaction', transaction => {
5051
expect(transaction.status).toBe('ok');
52+
// eslint-disable-next-line deprecation/deprecation
5153
expect(transaction.tags).toEqual({
5254
'http.status_code': '200',
5355
});

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export { createCheckInEnvelope } from './checkin';
7171
export { hasTracingEnabled } from './utils/hasTracingEnabled';
7272
export { isSentryRequestUrl } from './utils/isSentryRequestUrl';
7373
export { handleCallbackErrors } from './utils/handleCallbackErrors';
74-
export { spanToTraceHeader } from './utils/spanUtils';
74+
export { spanToTraceHeader, spanGetAttributes } from './utils/spanUtils';
7575
export { DEFAULT_ENVIRONMENT } from './constants';
7676
export { ModuleMetadata } from './integrations/metadata';
7777
export { RequestData } from './integrations/requestdata';

packages/core/src/tracing/idletransaction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export class IdleTransaction extends Transaction {
145145
this.activities = {};
146146

147147
if (this.op === 'ui.action.click') {
148+
// eslint-disable-next-line deprecation/deprecation
148149
this.setTag(FINISH_REASON_TAG, this._finishReason);
149150
}
150151

packages/core/src/tracing/span.ts

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
import { dropUndefinedKeys, logger, timestampInSeconds, uuid4 } from '@sentry/utils';
1414

1515
import { DEBUG_BUILD } from '../debug-build';
16-
import { spanToTraceContext, spanToTraceHeader } from '../utils/spanUtils';
16+
import { spanGetAttributes, spanToTraceContext, spanToTraceHeader } from '../utils/spanUtils';
1717
import { ensureTimestampInSeconds } from './utils';
1818

1919
/**
@@ -97,21 +97,18 @@ export class Span implements SpanInterface {
9797
public description?: string;
9898

9999
/**
100-
* @inheritDoc
100+
* Tags for the span.
101+
* @deprecated Use `getSpanAttributes(span)` instead.
101102
*/
102103
public tags: { [key: string]: Primitive };
103104

104105
/**
105-
* @inheritDoc
106+
* Data for the span.
107+
* @deprecated Use `getSpanAttributes(span)` instead.
106108
*/
107109
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108110
public data: { [key: string]: any };
109111

110-
/**
111-
* @inheritDoc
112-
*/
113-
public attributes: SpanAttributes;
114-
115112
/**
116113
* List of spans that were finalized
117114
*/
@@ -132,6 +129,8 @@ export class Span implements SpanInterface {
132129
*/
133130
public origin?: SpanOrigin;
134131

132+
protected _attributes: SpanAttributes;
133+
135134
/**
136135
* You should never call the constructor manually, always use `Sentry.startTransaction()`
137136
* or call `startChild()` on an existing span.
@@ -143,9 +142,11 @@ export class Span implements SpanInterface {
143142
this.traceId = spanContext.traceId || uuid4();
144143
this.spanId = spanContext.spanId || uuid4().substring(16);
145144
this.startTimestamp = spanContext.startTimestamp || timestampInSeconds();
145+
// eslint-disable-next-line deprecation/deprecation
146146
this.tags = spanContext.tags || {};
147+
// eslint-disable-next-line deprecation/deprecation
147148
this.data = spanContext.data || {};
148-
this.attributes = spanContext.attributes || {};
149+
this._attributes = spanContext.attributes || {};
149150
this.instrumenter = spanContext.instrumenter || 'sentry';
150151
this.origin = spanContext.origin || 'manual';
151152

@@ -173,6 +174,25 @@ export class Span implements SpanInterface {
173174
}
174175
}
175176

177+
// This rule conflicts with another eslint rule :(
178+
/* eslint-disable @typescript-eslint/member-ordering */
179+
180+
/**
181+
* Attributes for the span.
182+
* @deprecated Use `getSpanAttributes(span)` instead.
183+
*/
184+
public get attributes(): SpanAttributes {
185+
return this._attributes;
186+
}
187+
188+
/**
189+
* Attributes for the span.
190+
* @deprecated Use `setAttributes()` instead.
191+
*/
192+
public set attributes(attributes: SpanAttributes) {
193+
this._attributes = attributes;
194+
}
195+
176196
/** An alias for `description` of the Span. */
177197
public get name(): string {
178198
return this.description || '';
@@ -184,6 +204,8 @@ export class Span implements SpanInterface {
184204
this.updateName(name);
185205
}
186206

207+
/* eslint-enable @typescript-eslint/member-ordering */
208+
187209
/**
188210
* @inheritDoc
189211
*/
@@ -218,18 +240,29 @@ export class Span implements SpanInterface {
218240
}
219241

220242
/**
221-
* @inheritDoc
243+
* Sets the tag attribute on the current span.
244+
*
245+
* Can also be used to unset a tag, by passing `undefined`.
246+
*
247+
* @param key Tag key
248+
* @param value Tag value
249+
* @deprecated Use `setAttribute()` instead.
222250
*/
223251
public setTag(key: string, value: Primitive): this {
252+
// eslint-disable-next-line deprecation/deprecation
224253
this.tags = { ...this.tags, [key]: value };
225254
return this;
226255
}
227256

228257
/**
229-
* @inheritDoc
258+
* Sets the data attribute on the current span
259+
* @param key Data key
260+
* @param value Data value
261+
* @deprecated Use `setAttribute()` instead.
230262
*/
231263
// eslint-disable-next-line @typescript-eslint/no-explicit-any
232264
public setData(key: string, value: any): this {
265+
// eslint-disable-next-line deprecation/deprecation
233266
this.data = { ...this.data, [key]: value };
234267
return this;
235268
}
@@ -238,9 +271,9 @@ export class Span implements SpanInterface {
238271
public setAttribute(key: string, value: SpanAttributeValue | undefined): void {
239272
if (value === undefined) {
240273
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
241-
delete this.attributes[key];
274+
delete this._attributes[key];
242275
} else {
243-
this.attributes[key] = value;
276+
this._attributes[key] = value;
244277
}
245278
}
246279

@@ -261,7 +294,9 @@ export class Span implements SpanInterface {
261294
* @inheritDoc
262295
*/
263296
public setHttpStatus(httpStatus: number): this {
297+
// eslint-disable-next-line deprecation/deprecation
264298
this.setTag('http.status_code', String(httpStatus));
299+
// eslint-disable-next-line deprecation/deprecation
265300
this.setData('http.response.status_code', httpStatus);
266301
const spanStatus = spanStatusfromHttpCode(httpStatus);
267302
if (spanStatus !== 'unknown_error') {
@@ -329,7 +364,7 @@ export class Span implements SpanInterface {
329364
*/
330365
public toContext(): SpanContext {
331366
return dropUndefinedKeys({
332-
data: this._getData(),
367+
data: spanGetAttributes(this),
333368
description: this.description,
334369
endTimestamp: this.endTimestamp,
335370
op: this.op,
@@ -338,6 +373,7 @@ export class Span implements SpanInterface {
338373
spanId: this.spanId,
339374
startTimestamp: this.startTimestamp,
340375
status: this.status,
376+
// eslint-disable-next-line deprecation/deprecation
341377
tags: this.tags,
342378
traceId: this.traceId,
343379
});
@@ -347,6 +383,7 @@ export class Span implements SpanInterface {
347383
* @inheritDoc
348384
*/
349385
public updateWithContext(spanContext: SpanContext): this {
386+
// eslint-disable-next-line deprecation/deprecation
350387
this.data = spanContext.data || {};
351388
this.description = spanContext.description;
352389
this.endTimestamp = spanContext.endTimestamp;
@@ -356,6 +393,7 @@ export class Span implements SpanInterface {
356393
this.spanId = spanContext.spanId || this.spanId;
357394
this.startTimestamp = spanContext.startTimestamp || this.startTimestamp;
358395
this.status = spanContext.status;
396+
// eslint-disable-next-line deprecation/deprecation
359397
this.tags = spanContext.tags || {};
360398
this.traceId = spanContext.traceId || this.traceId;
361399

@@ -387,49 +425,20 @@ export class Span implements SpanInterface {
387425
origin?: SpanOrigin;
388426
} {
389427
return dropUndefinedKeys({
390-
data: this._getData(),
428+
data: spanGetAttributes(this),
391429
description: this.description,
392430
op: this.op,
393431
parent_span_id: this.parentSpanId,
394432
span_id: this.spanId,
395433
start_timestamp: this.startTimestamp,
396434
status: this.status,
435+
// eslint-disable-next-line deprecation/deprecation
397436
tags: Object.keys(this.tags).length > 0 ? this.tags : undefined,
398437
timestamp: this.endTimestamp,
399438
trace_id: this.traceId,
400439
origin: this.origin,
401440
});
402441
}
403-
404-
/**
405-
* Get the merged data for this span.
406-
* For now, this combines `data` and `attributes` together,
407-
* until eventually we can ingest `attributes` directly.
408-
*/
409-
private _getData():
410-
| {
411-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
412-
[key: string]: any;
413-
}
414-
| undefined {
415-
const { data, attributes } = this;
416-
417-
const hasData = Object.keys(data).length > 0;
418-
const hasAttributes = Object.keys(attributes).length > 0;
419-
420-
if (!hasData && !hasAttributes) {
421-
return undefined;
422-
}
423-
424-
if (hasData && hasAttributes) {
425-
return {
426-
...data,
427-
...attributes,
428-
};
429-
}
430-
431-
return hasData ? data : attributes;
432-
}
433442
}
434443

435444
export type SpanStatusType =

packages/core/src/tracing/transaction.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ export class Transaction extends SpanClass implements TransactionInterface {
119119
}
120120

121121
/**
122-
* @inheritDoc
122+
* Set the context of a transaction event.
123+
* @deprecated Use either `.setAttribute()`, or set the context on the scope before creating the transaction.
123124
*/
124125
public setContext(key: string, context: Context | null): void {
125126
if (context === null) {
@@ -288,6 +289,7 @@ export class Transaction extends SpanClass implements TransactionInterface {
288289
},
289290
spans: finishedSpans,
290291
start_timestamp: this.startTimestamp,
292+
// eslint-disable-next-line deprecation/deprecation
291293
tags: this.tags,
292294
timestamp: this.endTimestamp,
293295
transaction: this.name,

packages/core/src/utils/prepareEvent.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { DEFAULT_ENVIRONMENT } from '../constants';
1515
import { getGlobalEventProcessors, notifyEventProcessors } from '../eventProcessors';
1616
import { Scope, getGlobalScope } from '../scope';
1717
import { applyScopeDataToEvent, mergeScopeData } from './applyScopeDataToEvent';
18+
import { spanGetAttributes } from './spanUtils';
1819

1920
/**
2021
* This type makes sure that we get either a CaptureContext, OR an EventHint.
@@ -326,10 +327,14 @@ function normalizeEvent(event: Event | null, depth: number, maxBreadth: number):
326327
// event.spans[].data may contain circular/dangerous data so we need to normalize it
327328
if (event.spans) {
328329
normalized.spans = event.spans.map(span => {
329-
// We cannot use the spread operator here because `toJSON` on `span` is non-enumerable
330-
if (span.data) {
331-
span.data = normalize(span.data, depth, maxBreadth);
330+
const data = spanGetAttributes(span);
331+
332+
if (data) {
333+
// This is a bit weird, as we generally have `Span` instances here, but to be safe we do not assume so
334+
// eslint-disable-next-line deprecation/deprecation
335+
span.data = data;
332336
}
337+
333338
return span;
334339
});
335340
}

0 commit comments

Comments
 (0)