Skip to content

Commit a04e780

Browse files
mydeaanonrig
andauthored
feat(core): Add span.end() to replace span.finish() (#9954)
To align with OTEL spans. This also deprecates `span.finish()`, and also makes sure that `end()` accepts both ms as well as s timestmaps (to align this with OTEL in the future, which accepts ms only). --------- Co-authored-by: Yagiz Nizipli <[email protected]>
1 parent 12a4fce commit a04e780

File tree

105 files changed

+472
-299
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+472
-299
lines changed

packages/angular/src/tracing.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class TraceService implements OnDestroy {
8282

8383
if (activeTransaction) {
8484
if (this._routingSpan) {
85-
this._routingSpan.finish();
85+
this._routingSpan.end();
8686
}
8787
this._routingSpan = activeTransaction.startChild({
8888
description: `${navigationEvent.url}`,
@@ -131,7 +131,7 @@ export class TraceService implements OnDestroy {
131131
if (this._routingSpan) {
132132
runOutsideAngular(() => {
133133
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
134-
this._routingSpan!.finish();
134+
this._routingSpan!.end();
135135
});
136136
this._routingSpan = null;
137137
}
@@ -196,7 +196,7 @@ export class TraceDirective implements OnInit, AfterViewInit {
196196
*/
197197
public ngAfterViewInit(): void {
198198
if (this._tracingSpan) {
199-
this._tracingSpan.finish();
199+
this._tracingSpan.end();
200200
}
201201
}
202202
}
@@ -239,7 +239,7 @@ export function TraceClassDecorator(): ClassDecorator {
239239
// eslint-disable-next-line @typescript-eslint/no-explicit-any
240240
target.prototype.ngAfterViewInit = function (...args: any[]): ReturnType<typeof originalAfterViewInit> {
241241
if (tracingSpan) {
242-
tracingSpan.finish();
242+
tracingSpan.end();
243243
}
244244
if (originalAfterViewInit) {
245245
return originalAfterViewInit.apply(this, args);

packages/angular/test/tracing.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ describe('Angular Tracing', () => {
154154

155155
const finishMock = jest.fn();
156156
transaction.startChild = jest.fn(() => ({
157-
finish: finishMock,
157+
end: finishMock,
158158
}));
159159

160160
await env.navigateInAngular('/');
@@ -173,7 +173,7 @@ describe('Angular Tracing', () => {
173173

174174
const finishMock = jest.fn();
175175
transaction.startChild = jest.fn(() => ({
176-
finish: finishMock,
176+
end: finishMock,
177177
}));
178178

179179
await env.navigateInAngular('/');
@@ -199,7 +199,7 @@ describe('Angular Tracing', () => {
199199

200200
const finishMock = jest.fn();
201201
transaction.startChild = jest.fn(() => ({
202-
finish: finishMock,
202+
end: finishMock,
203203
}));
204204

205205
await env.navigateInAngular('/somewhere');
@@ -233,7 +233,7 @@ describe('Angular Tracing', () => {
233233

234234
const finishMock = jest.fn();
235235
transaction.startChild = jest.fn(() => ({
236-
finish: finishMock,
236+
end: finishMock,
237237
}));
238238

239239
await env.navigateInAngular('/cancel');
@@ -376,7 +376,7 @@ describe('Angular Tracing', () => {
376376
});
377377

378378
transaction.startChild = jest.fn(() => ({
379-
finish: finishMock,
379+
end: finishMock,
380380
}));
381381

382382
directive.componentName = 'test-component';
@@ -403,7 +403,7 @@ describe('Angular Tracing', () => {
403403
});
404404

405405
transaction.startChild = jest.fn(() => ({
406-
finish: finishMock,
406+
end: finishMock,
407407
}));
408408

409409
directive.ngOnInit();
@@ -437,7 +437,7 @@ describe('Angular Tracing', () => {
437437
it('Instruments `ngOnInit` and `ngAfterViewInit` methods of the decorated class', async () => {
438438
const finishMock = jest.fn();
439439
const startChildMock = jest.fn(() => ({
440-
finish: finishMock,
440+
end: finishMock,
441441
}));
442442

443443
const customStartTransaction = jest.fn((ctx: any) => {

packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/subject.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ async function run() {
1111
await new Promise(resolve => setTimeout(resolve, 1));
1212

1313
// span_1 finishes
14-
span_1.finish();
14+
span_1.end();
1515

1616
// span_2 doesn't finish
1717
const span_2 = transaction.startChild({ op: 'span_2' });
@@ -24,12 +24,12 @@ async function run() {
2424
const span_4 = span_3.startChild({ op: 'span_4', data: { qux: 'quux' } });
2525

2626
// span_5 is another child of span_3 but finishes.
27-
const span_5 = span_3.startChild({ op: 'span_5' }).finish();
27+
const span_5 = span_3.startChild({ op: 'span_5' }).end();
2828

2929
// span_3 also finishes
30-
span_3.finish();
30+
span_3.end();
3131

32-
transaction.finish();
32+
transaction.end();
3333
}
3434

3535
run();

packages/browser-integration-tests/suites/public-api/startTransaction/circular_data/subject.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ const circularObject = chicken;
77
const transaction = Sentry.startTransaction({ name: 'circular_object_test_transaction', data: circularObject });
88
const span = transaction.startChild({ op: 'circular_object_test_span', data: circularObject });
99

10-
span.finish();
11-
transaction.finish();
10+
span.end();
11+
transaction.end();

packages/browser-integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ transaction.setMeasurement('metric.bar', 1337, 'nanoseconds');
55
transaction.setMeasurement('metric.baz', 99, 's');
66
transaction.setMeasurement('metric.baz', 1);
77

8-
transaction.finish();
8+
transaction.end();

packages/browser/src/profiling/hubextensions.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,34 +143,34 @@ export function startProfileForTransaction(transaction: Transaction): Transactio
143143
onProfileHandler();
144144
}, MAX_PROFILE_DURATION_MS);
145145

146-
// We need to reference the original finish call to avoid creating an infinite loop
147-
const originalFinish = transaction.finish.bind(transaction);
146+
// We need to reference the original end call to avoid creating an infinite loop
147+
const originalEnd = transaction.end.bind(transaction);
148148

149149
/**
150150
* Wraps startTransaction and stopTransaction with profiling related logic.
151151
* startProfiling is called after the call to startTransaction in order to avoid our own code from
152152
* being profiled. Because of that same reason, stopProfiling is called before the call to stopTransaction.
153153
*/
154-
function profilingWrappedTransactionFinish(): Transaction {
154+
function profilingWrappedTransactionEnd(): Transaction {
155155
if (!transaction) {
156-
return originalFinish();
156+
return originalEnd();
157157
}
158158
// onProfileHandler should always return the same profile even if this is called multiple times.
159159
// Always call onProfileHandler to ensure stopProfiling is called and the timeout is cleared.
160160
void onProfileHandler().then(
161161
() => {
162162
transaction.setContext('profile', { profile_id: profileId, start_timestamp: startTimestamp });
163-
originalFinish();
163+
originalEnd();
164164
},
165165
() => {
166166
// If onProfileHandler fails, we still want to call the original finish method.
167-
originalFinish();
167+
originalEnd();
168168
},
169169
);
170170

171171
return transaction;
172172
}
173173

174-
transaction.finish = profilingWrappedTransactionFinish;
174+
transaction.end = profilingWrappedTransactionEnd;
175175
return transaction;
176176
}

packages/browser/test/unit/profiling/integration.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('BrowserProfilingIntegration', () => {
5252

5353
const currentTransaction = Sentry.getCurrentHub().getScope().getTransaction();
5454
expect(currentTransaction?.op).toBe('pageload');
55-
currentTransaction?.finish();
55+
currentTransaction?.end();
5656
await client?.flush(1000);
5757

5858
expect(send).toHaveBeenCalledTimes(1);

packages/core/src/exports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export function withScope<T>(callback: (scope: Scope) => T): T {
179179
*
180180
* Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded.
181181
*
182-
* The transaction must be finished with a call to its `.finish()` method, at which point the transaction with all its
182+
* The transaction must be finished with a call to its `.end()` method, at which point the transaction with all its
183183
* finished child spans will be sent to Sentry.
184184
*
185185
* NOTE: This function should only be used for *manual* instrumentation. Auto-instrumentation should call

packages/core/src/tracing/idletransaction.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Hub } from '../hub';
77
import type { Span } from './span';
88
import { SpanRecorder } from './span';
99
import { Transaction } from './transaction';
10+
import { ensureTimestampInSeconds } from './utils';
1011

1112
export const TRACING_DEFAULTS = {
1213
idleTimeout: 1000,
@@ -45,10 +46,12 @@ export class IdleTransactionSpanRecorder extends SpanRecorder {
4546
// We should make sure we do not push and pop activities for
4647
// the transaction that this span recorder belongs to.
4748
if (span.spanId !== this.transactionSpanId) {
48-
// We patch span.finish() to pop an activity after setting an endTimestamp.
49-
span.finish = (endTimestamp?: number) => {
50-
span.endTimestamp = typeof endTimestamp === 'number' ? endTimestamp : timestampInSeconds();
49+
// We patch span.end() to pop an activity after setting an endTimestamp.
50+
// eslint-disable-next-line @typescript-eslint/unbound-method
51+
const originalEnd = span.end;
52+
span.end = (...rest: unknown[]) => {
5153
this._popActivity(span.spanId);
54+
return originalEnd.apply(span, rest);
5255
};
5356

5457
// We should only push new activities if the span does not have an end timestamp.
@@ -129,13 +132,15 @@ export class IdleTransaction extends Transaction {
129132
if (!this._finished) {
130133
this.setStatus('deadline_exceeded');
131134
this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[3];
132-
this.finish();
135+
this.end();
133136
}
134137
}, this._finalTimeout);
135138
}
136139

137140
/** {@inheritDoc} */
138-
public finish(endTimestamp: number = timestampInSeconds()): string | undefined {
141+
public end(endTimestamp: number = timestampInSeconds()): string | undefined {
142+
const endTimestampInS = ensureTimestampInSeconds(endTimestamp);
143+
139144
this._finished = true;
140145
this.activities = {};
141146

@@ -145,7 +150,7 @@ export class IdleTransaction extends Transaction {
145150

146151
if (this.spanRecorder) {
147152
DEBUG_BUILD &&
148-
logger.log('[Tracing] finishing IdleTransaction', new Date(endTimestamp * 1000).toISOString(), this.op);
153+
logger.log('[Tracing] finishing IdleTransaction', new Date(endTimestampInS * 1000).toISOString(), this.op);
149154

150155
for (const callback of this._beforeFinishCallbacks) {
151156
callback(this, endTimestamp);
@@ -159,13 +164,13 @@ export class IdleTransaction extends Transaction {
159164

160165
// We cancel all pending spans with status "cancelled" to indicate the idle transaction was finished early
161166
if (!span.endTimestamp) {
162-
span.endTimestamp = endTimestamp;
167+
span.endTimestamp = endTimestampInS;
163168
span.setStatus('cancelled');
164169
DEBUG_BUILD &&
165170
logger.log('[Tracing] cancelling span since transaction ended early', JSON.stringify(span, undefined, 2));
166171
}
167172

168-
const spanStartedBeforeTransactionFinish = span.startTimestamp < endTimestamp;
173+
const spanStartedBeforeTransactionFinish = span.startTimestamp < endTimestampInS;
169174

170175
// Add a delta with idle timeout so that we prevent false positives
171176
const timeoutWithMarginOfError = (this._finalTimeout + this._idleTimeout) / 1000;
@@ -196,7 +201,7 @@ export class IdleTransaction extends Transaction {
196201
}
197202
}
198203

199-
return super.finish(endTimestamp);
204+
return super.end(endTimestamp);
200205
}
201206

202207
/**
@@ -244,7 +249,7 @@ export class IdleTransaction extends Transaction {
244249
* with the last child span.
245250
*/
246251
public cancelIdleTimeout(
247-
endTimestamp?: Parameters<IdleTransaction['finish']>[0],
252+
endTimestamp?: Parameters<IdleTransaction['end']>[0],
248253
{
249254
restartOnChildSpanChange,
250255
}: {
@@ -260,7 +265,7 @@ export class IdleTransaction extends Transaction {
260265

261266
if (Object.keys(this.activities).length === 0 && this._idleTimeoutCanceledPermanently) {
262267
this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[5];
263-
this.finish(endTimestamp);
268+
this.end(endTimestamp);
264269
}
265270
}
266271
}
@@ -281,12 +286,12 @@ export class IdleTransaction extends Transaction {
281286
/**
282287
* Restarts idle timeout, if there is no running idle timeout it will start one.
283288
*/
284-
private _restartIdleTimeout(endTimestamp?: Parameters<IdleTransaction['finish']>[0]): void {
289+
private _restartIdleTimeout(endTimestamp?: Parameters<IdleTransaction['end']>[0]): void {
285290
this.cancelIdleTimeout();
286291
this._idleTimeoutID = setTimeout(() => {
287292
if (!this._finished && Object.keys(this.activities).length === 0) {
288293
this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[1];
289-
this.finish(endTimestamp);
294+
this.end(endTimestamp);
290295
}
291296
}, this._idleTimeout);
292297
}
@@ -318,7 +323,7 @@ export class IdleTransaction extends Transaction {
318323
const endTimestamp = timestampInSeconds();
319324
if (this._idleTimeoutCanceledPermanently) {
320325
this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[5];
321-
this.finish(endTimestamp);
326+
this.end(endTimestamp);
322327
} else {
323328
// We need to add the timeout here to have the real endtimestamp of the transaction
324329
// Remember timestampInSeconds is in seconds, timeout is in ms
@@ -351,7 +356,7 @@ export class IdleTransaction extends Transaction {
351356
DEBUG_BUILD && logger.log('[Tracing] Transaction finished because of no change for 3 heart beats');
352357
this.setStatus('deadline_exceeded');
353358
this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[0];
354-
this.finish();
359+
this.end();
355360
} else {
356361
this._pingHeartbeat();
357362
}

packages/core/src/tracing/span.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
import { dropUndefinedKeys, generateSentryTraceHeader, logger, timestampInSeconds, uuid4 } from '@sentry/utils';
1212

1313
import { DEBUG_BUILD } from '../debug-build';
14+
import { ensureTimestampInSeconds } from './utils';
1415

1516
/**
1617
* Keeps track of finished spans for a given transaction
@@ -259,8 +260,15 @@ export class Span implements SpanInterface {
259260

260261
/**
261262
* @inheritDoc
263+
*
264+
* @deprecated Use `.end()` instead.
262265
*/
263266
public finish(endTimestamp?: number): void {
267+
return this.end(endTimestamp);
268+
}
269+
270+
/** @inheritdoc */
271+
public end(endTimestamp?: number): void {
264272
if (
265273
DEBUG_BUILD &&
266274
// Don't call this for transactions
@@ -273,7 +281,8 @@ export class Span implements SpanInterface {
273281
}
274282
}
275283

276-
this.endTimestamp = typeof endTimestamp === 'number' ? endTimestamp : timestampInSeconds();
284+
this.endTimestamp =
285+
typeof endTimestamp === 'number' ? ensureTimestampInSeconds(endTimestamp) : timestampInSeconds();
277286
}
278287

279288
/**

packages/core/src/tracing/trace.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function trace<T>(
3939
scope.setSpan(activeSpan);
4040

4141
function finishAndSetSpan(): void {
42-
activeSpan && activeSpan.finish();
42+
activeSpan && activeSpan.end();
4343
scope.setSpan(parentSpan);
4444
}
4545

@@ -97,7 +97,7 @@ export function startSpan<T>(context: TransactionContext, callback: (span: Span
9797
scope.setSpan(activeSpan);
9898

9999
function finishAndSetSpan(): void {
100-
activeSpan && activeSpan.finish();
100+
activeSpan && activeSpan.end();
101101
scope.setSpan(parentSpan);
102102
}
103103

@@ -157,7 +157,7 @@ export function startSpanManual<T>(
157157
scope.setSpan(activeSpan);
158158

159159
function finishAndSetSpan(): void {
160-
activeSpan && activeSpan.finish();
160+
activeSpan && activeSpan.end();
161161
scope.setSpan(parentSpan);
162162
}
163163

0 commit comments

Comments
 (0)