Skip to content

Commit 7d7f0db

Browse files
authored
fix(apm/tracing): update pageload op transaction start timestamps (getsentry#2773)
1 parent e771b41 commit 7d7f0db

File tree

4 files changed

+91
-16
lines changed

4 files changed

+91
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66
- [react] feat: Add instrumentation for React Router v3 (#2759)
77
- [apm/tracing] fix: Make sure fetch requests are being timed correctly (#2772)
8+
- [apm/tracing] fix: Make sure pageload transactions start timestamps are correctly generated (#2773)
89

910
## 5.20.0
1011

packages/apm/src/integrations/tracing.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ export class Tracing implements Integration {
581581

582582
// tslint:disable-next-line: completed-docs
583583
function addPerformanceNavigationTiming(parent: Span, entry: { [key: string]: number }, event: string): void {
584-
parent.startChild({
584+
_startChild(parent, {
585585
description: event,
586586
endTimestamp: timeOrigin + Tracing._msToSec(entry[`${event}End`]),
587587
op: 'browser',
@@ -591,14 +591,14 @@ export class Tracing implements Integration {
591591

592592
// tslint:disable-next-line: completed-docs
593593
function addRequest(parent: Span, entry: { [key: string]: number }): void {
594-
parent.startChild({
594+
_startChild(parent, {
595595
description: 'request',
596596
endTimestamp: timeOrigin + Tracing._msToSec(entry.responseEnd),
597597
op: 'browser',
598598
startTimestamp: timeOrigin + Tracing._msToSec(entry.requestStart),
599599
});
600600

601-
parent.startChild({
601+
_startChild(parent, {
602602
description: 'response',
603603
endTimestamp: timeOrigin + Tracing._msToSec(entry.responseEnd),
604604
op: 'browser',
@@ -648,12 +648,12 @@ export class Tracing implements Integration {
648648
case 'mark':
649649
case 'paint':
650650
case 'measure':
651-
const mark = transactionSpan.startChild({
651+
const mark = _startChild(transactionSpan, {
652652
description: entry.name,
653+
endTimestamp: timeOrigin + startTime + duration,
653654
op: entry.entryType,
655+
startTimestamp: timeOrigin + startTime,
654656
});
655-
mark.startTimestamp = timeOrigin + startTime;
656-
mark.endTimestamp = mark.startTimestamp + duration;
657657
if (tracingInitMarkStartTime === undefined && entry.name === 'sentry-tracing-init') {
658658
tracingInitMarkStartTime = mark.startTimestamp;
659659
}
@@ -663,12 +663,12 @@ export class Tracing implements Integration {
663663
// we already instrument based on fetch and xhr, so we don't need to
664664
// duplicate spans here.
665665
if (entry.initiatorType !== 'xmlhttprequest' && entry.initiatorType !== 'fetch') {
666-
const resource = transactionSpan.startChild({
666+
const resource = _startChild(transactionSpan, {
667667
description: `${entry.initiatorType} ${resourceName}`,
668+
endTimestamp: timeOrigin + startTime + duration,
668669
op: `resource`,
670+
startTimestamp: timeOrigin + startTime,
669671
});
670-
resource.startTimestamp = timeOrigin + startTime;
671-
resource.endTimestamp = resource.startTimestamp + duration;
672672
// We remember the entry script end time to calculate the difference to the first init mark
673673
if (entryScriptStartEndTime === undefined && (entryScriptSrc || '').indexOf(resourceName) > -1) {
674674
entryScriptStartEndTime = resource.endTimestamp;
@@ -681,7 +681,7 @@ export class Tracing implements Integration {
681681
});
682682

683683
if (entryScriptStartEndTime !== undefined && tracingInitMarkStartTime !== undefined) {
684-
transactionSpan.startChild({
684+
_startChild(transactionSpan, {
685685
description: 'evaluation',
686686
endTimestamp: tracingInitMarkStartTime,
687687
op: `script`,
@@ -1046,3 +1046,19 @@ function historyCallback(_: { [key: string]: any }): void {
10461046
});
10471047
}
10481048
}
1049+
1050+
/**
1051+
* Helper function to start child on transactions. This function will make sure that the transaction will
1052+
* use the start timestamp of the created child span if it is earlier than the transactions actual
1053+
* start timestamp.
1054+
*/
1055+
export function _startChild(parent: Span, { startTimestamp, ...ctx }: SpanContext): Span {
1056+
if (startTimestamp && parent.startTimestamp > startTimestamp) {
1057+
parent.startTimestamp = startTimestamp;
1058+
}
1059+
1060+
return parent.startChild({
1061+
startTimestamp,
1062+
...ctx,
1063+
});
1064+
}

packages/tracing/src/browser/metrics.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { SpanContext } from '@sentry/types';
12
import { getGlobalObject, logger } from '@sentry/utils';
23

4+
import { Span } from '../span';
35
import { Transaction } from '../transaction';
46

57
import { msToSec } from './utils';
@@ -162,7 +164,7 @@ export class MetricsInstrumentation {
162164
});
163165

164166
if (entryScriptStartTimestamp !== undefined && tracingInitMarkStartTime !== undefined) {
165-
transaction.startChild({
167+
_startChild(transaction, {
166168
description: 'evaluation',
167169
endTimestamp: tracingInitMarkStartTime,
168170
op: 'script',
@@ -195,7 +197,7 @@ function addMeasureSpans(
195197
const measureStartTimestamp = timeOrigin + startTime;
196198
const measureEndTimestamp = measureStartTimestamp + duration;
197199

198-
transaction.startChild({
200+
_startChild(transaction, {
199201
description: entry.name as string,
200202
endTimestamp: measureEndTimestamp,
201203
op: entry.entryType as string,
@@ -223,7 +225,7 @@ function addResourceSpans(
223225
const startTimestamp = timeOrigin + startTime;
224226
const endTimestamp = startTimestamp + duration;
225227

226-
transaction.startChild({
228+
_startChild(transaction, {
227229
description: `${entry.initiatorType} ${resourceName}`,
228230
endTimestamp,
229231
op: 'resource',
@@ -245,7 +247,7 @@ function addPerformanceNavigationTiming(
245247
if (!start || !end) {
246248
return;
247249
}
248-
transaction.startChild({
250+
_startChild(transaction, {
249251
description: event,
250252
endTimestamp: timeOrigin + msToSec(end),
251253
op: 'browser',
@@ -255,17 +257,33 @@ function addPerformanceNavigationTiming(
255257

256258
/** Create request and response related spans */
257259
function addRequest(transaction: Transaction, entry: Record<string, any>, timeOrigin: number): void {
258-
transaction.startChild({
260+
_startChild(transaction, {
259261
description: 'request',
260262
endTimestamp: timeOrigin + msToSec(entry.responseEnd as number),
261263
op: 'browser',
262264
startTimestamp: timeOrigin + msToSec(entry.requestStart as number),
263265
});
264266

265-
transaction.startChild({
267+
_startChild(transaction, {
266268
description: 'response',
267269
endTimestamp: timeOrigin + msToSec(entry.responseEnd as number),
268270
op: 'browser',
269271
startTimestamp: timeOrigin + msToSec(entry.responseStart as number),
270272
});
271273
}
274+
275+
/**
276+
* Helper function to start child on transactions. This function will make sure that the transaction will
277+
* use the start timestamp of the created child span if it is earlier than the transactions actual
278+
* start timestamp.
279+
*/
280+
export function _startChild(transaction: Transaction, { startTimestamp, ...ctx }: SpanContext): Span {
281+
if (startTimestamp && transaction.startTimestamp > startTimestamp) {
282+
transaction.startTimestamp = startTimestamp;
283+
}
284+
285+
return transaction.startChild({
286+
startTimestamp,
287+
...ctx,
288+
});
289+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Span, Transaction } from '../../src';
2+
import { _startChild } from '../../src/browser/metrics';
3+
4+
describe('_startChild()', () => {
5+
it('creates a span with given properties', () => {
6+
const transaction = new Transaction({ name: 'test' });
7+
const span = _startChild(transaction, {
8+
description: 'evaluation',
9+
op: 'script',
10+
});
11+
12+
expect(span).toBeInstanceOf(Span);
13+
expect(span.description).toBe('evaluation');
14+
expect(span.op).toBe('script');
15+
});
16+
17+
it('adjusts the start timestamp if child span starts before transaction', () => {
18+
const transaction = new Transaction({ name: 'test', startTimestamp: 123 });
19+
const span = _startChild(transaction, {
20+
description: 'script.js',
21+
op: 'resource',
22+
startTimestamp: 100,
23+
});
24+
25+
expect(transaction.startTimestamp).toEqual(span.startTimestamp);
26+
expect(transaction.startTimestamp).toEqual(100);
27+
});
28+
29+
it('does not adjust start timestamp if child span starts after transaction', () => {
30+
const transaction = new Transaction({ name: 'test', startTimestamp: 123 });
31+
const span = _startChild(transaction, {
32+
description: 'script.js',
33+
op: 'resource',
34+
startTimestamp: 150,
35+
});
36+
37+
expect(transaction.startTimestamp).not.toEqual(span.startTimestamp);
38+
expect(transaction.startTimestamp).toEqual(123);
39+
});
40+
});

0 commit comments

Comments
 (0)