Skip to content

Commit 4a0e964

Browse files
authored
fix(web-vitals): Fix TTFB capture in Safari (#3106)
1 parent 355cb77 commit 4a0e964

File tree

3 files changed

+19
-64
lines changed

3 files changed

+19
-64
lines changed

packages/tracing/src/browser/metrics.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export class MetricsInstrumentation {
136136
if (transaction.op === 'pageload') {
137137
// normalize applicable web vital values to be relative to transaction.startTimestamp
138138

139-
const timeOrigin = msToSec(performance.timeOrigin);
139+
const timeOrigin = msToSec(browserPerformanceTimeOrigin);
140140

141141
['fcp', 'fp', 'lcp', 'ttfb'].forEach(name => {
142142
if (!this._measurements[name] || timeOrigin >= transaction.startTimestamp) {
@@ -150,12 +150,10 @@ export class MetricsInstrumentation {
150150
const oldValue = this._measurements[name].value;
151151
const measurementTimestamp = timeOrigin + msToSec(oldValue);
152152
// normalizedValue should be in milliseconds
153-
const normalizedValue = (measurementTimestamp - transaction.startTimestamp) * 1000;
153+
const normalizedValue = Math.abs((measurementTimestamp - transaction.startTimestamp) * 1000);
154154

155155
const delta = normalizedValue - oldValue;
156-
logger.log(
157-
`[Measurements] Normalized ${name} from ${this._measurements[name].value} to ${normalizedValue} (${delta})`,
158-
);
156+
logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`);
159157

160158
this._measurements[name].value = normalizedValue;
161159
});

packages/tracing/src/browser/web-vitals/getTTFB.ts

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,10 @@
1717
import { getGlobalObject } from '@sentry/utils';
1818

1919
import { initMetric } from './lib/initMetric';
20-
import { ReportHandler } from './types';
20+
import { NavigationTimingPolyfillEntry, ReportHandler } from './types';
2121

2222
const global = getGlobalObject<Window>();
2323

24-
interface NavigationEntryShim {
25-
// From `PerformanceNavigationTimingEntry`.
26-
entryType: string;
27-
startTime: number;
28-
29-
// From `performance.timing`.
30-
connectEnd?: number;
31-
connectStart?: number;
32-
domComplete?: number;
33-
domContentLoadedEventEnd?: number;
34-
domContentLoadedEventStart?: number;
35-
domInteractive?: number;
36-
domainLookupEnd?: number;
37-
domainLookupStart?: number;
38-
fetchStart?: number;
39-
loadEventEnd?: number;
40-
loadEventStart?: number;
41-
redirectEnd?: number;
42-
redirectStart?: number;
43-
requestStart?: number;
44-
responseEnd?: number;
45-
responseStart?: number;
46-
secureConnectionStart?: number;
47-
unloadEventEnd?: number;
48-
unloadEventStart?: number;
49-
}
50-
51-
type PerformanceTimingKeys =
52-
| 'connectEnd'
53-
| 'connectStart'
54-
| 'domComplete'
55-
| 'domContentLoadedEventEnd'
56-
| 'domContentLoadedEventStart'
57-
| 'domInteractive'
58-
| 'domainLookupEnd'
59-
| 'domainLookupStart'
60-
| 'fetchStart'
61-
| 'loadEventEnd'
62-
| 'loadEventStart'
63-
| 'redirectEnd'
64-
| 'redirectStart'
65-
| 'requestStart'
66-
| 'responseEnd'
67-
| 'responseStart'
68-
| 'secureConnectionStart'
69-
| 'unloadEventEnd'
70-
| 'unloadEventStart';
71-
7224
const afterLoad = (callback: () => void): void => {
7325
if (document.readyState === 'complete') {
7426
// Queue a task so the callback runs after `loadEventEnd`.
@@ -79,27 +31,22 @@ const afterLoad = (callback: () => void): void => {
7931
}
8032
};
8133

82-
const getNavigationEntryFromPerformanceTiming = (): PerformanceNavigationTiming => {
34+
const getNavigationEntryFromPerformanceTiming = (): NavigationTimingPolyfillEntry => {
8335
// Really annoying that TypeScript errors when using `PerformanceTiming`.
84-
// Note: browsers that do not support navigation entries will fall back to using performance.timing
85-
// (with the timestamps converted from epoch time to DOMHighResTimeStamp).
8636
// eslint-disable-next-line deprecation/deprecation
8737
const timing = global.performance.timing;
8838

89-
const navigationEntry: NavigationEntryShim = {
39+
const navigationEntry: { [key: string]: number | string } = {
9040
entryType: 'navigation',
9141
startTime: 0,
9242
};
9343

9444
for (const key in timing) {
9545
if (key !== 'navigationStart' && key !== 'toJSON') {
96-
navigationEntry[key as PerformanceTimingKeys] = Math.max(
97-
timing[key as PerformanceTimingKeys] - timing.navigationStart,
98-
0,
99-
);
46+
navigationEntry[key] = Math.max((timing[key as keyof PerformanceTiming] as number) - timing.navigationStart, 0);
10047
}
10148
}
102-
return navigationEntry as PerformanceNavigationTiming;
49+
return navigationEntry as NavigationTimingPolyfillEntry;
10350
};
10451

10552
export const getTTFB = (onReport: ReportHandler): void => {
@@ -114,7 +61,6 @@ export const getTTFB = (onReport: ReportHandler): void => {
11461
metric.value = metric.delta = (navigationEntry as PerformanceNavigationTiming).responseStart;
11562

11663
metric.entries = [navigationEntry];
117-
metric.isFinal = true;
11864

11965
onReport(metric);
12066
} catch (error) {

packages/tracing/src/browser/web-vitals/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,14 @@ interface NetworkInformation extends EventTarget {
8282
export interface NavigatorDeviceMemory {
8383
readonly deviceMemory?: number;
8484
}
85+
86+
export type NavigationTimingPolyfillEntry = Omit<
87+
PerformanceNavigationTiming,
88+
| 'initiatorType'
89+
| 'nextHopProtocol'
90+
| 'redirectCount'
91+
| 'transferSize'
92+
| 'encodedBodySize'
93+
| 'decodedBodySize'
94+
| 'toJSON'
95+
>;

0 commit comments

Comments
 (0)