Skip to content

Commit 02742ef

Browse files
authored
fix(browser): Remove browserPerformanceTimeOrigin side-effects (#14025)
1 parent ce822ff commit 02742ef

File tree

14 files changed

+50
-47
lines changed

14 files changed

+50
-47
lines changed

packages/browser-utils/src/metrics/browserMetrics.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ interface StartTrackingWebVitalsOptions {
8383
*/
8484
export function startTrackingWebVitals({ recordClsStandaloneSpans }: StartTrackingWebVitalsOptions): () => void {
8585
const performance = getBrowserPerformanceAPI();
86-
if (performance && browserPerformanceTimeOrigin) {
86+
if (performance && browserPerformanceTimeOrigin()) {
8787
// @ts-expect-error we want to make sure all of these are available, even if TS is sure they are
8888
if (performance.mark) {
8989
WINDOW.performance.mark('sentry-tracing-init');
@@ -117,7 +117,7 @@ export function startTrackingLongTasks(): void {
117117
const { op: parentOp, start_timestamp: parentStartTimestamp } = spanToJSON(parent);
118118

119119
for (const entry of entries) {
120-
const startTime = msToSec((browserPerformanceTimeOrigin as number) + entry.startTime);
120+
const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);
121121
const duration = msToSec(entry.duration);
122122

123123
if (parentOp === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {
@@ -156,7 +156,7 @@ export function startTrackingLongAnimationFrames(): void {
156156
continue;
157157
}
158158

159-
const startTime = msToSec((browserPerformanceTimeOrigin as number) + entry.startTime);
159+
const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);
160160

161161
const { start_timestamp: parentStartTimestamp, op: parentOp } = spanToJSON(parent);
162162

@@ -167,7 +167,6 @@ export function startTrackingLongAnimationFrames(): void {
167167
// routing instrumentations
168168
continue;
169169
}
170-
171170
const duration = msToSec(entry.duration);
172171

173172
const attributes: SpanAttributes = {
@@ -210,7 +209,7 @@ export function startTrackingInteractions(): void {
210209
}
211210
for (const entry of entries) {
212211
if (entry.name === 'click') {
213-
const startTime = msToSec((browserPerformanceTimeOrigin as number) + entry.startTime);
212+
const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);
214213
const duration = msToSec(entry.duration);
215214

216215
const spanOptions: StartSpanOptions & Required<Pick<StartSpanOptions, 'attributes'>> = {
@@ -271,7 +270,7 @@ function _trackFID(): () => void {
271270
return;
272271
}
273272

274-
const timeOrigin = msToSec(browserPerformanceTimeOrigin as number);
273+
const timeOrigin = msToSec(browserPerformanceTimeOrigin() as number);
275274
const startTime = msToSec(entry.startTime);
276275
_measurements['fid'] = { value: metric.value, unit: 'millisecond' };
277276
_measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' };
@@ -300,12 +299,13 @@ interface AddPerformanceEntriesOptions {
300299
/** Add performance related spans to a transaction */
301300
export function addPerformanceEntries(span: Span, options: AddPerformanceEntriesOptions): void {
302301
const performance = getBrowserPerformanceAPI();
303-
if (!performance?.getEntries || !browserPerformanceTimeOrigin) {
302+
const origin = browserPerformanceTimeOrigin();
303+
if (!performance?.getEntries || !origin) {
304304
// Gatekeeper if performance API not available
305305
return;
306306
}
307307

308-
const timeOrigin = msToSec(browserPerformanceTimeOrigin);
308+
const timeOrigin = msToSec(origin);
309309

310310
const performanceEntries = performance.getEntries();
311311

packages/browser-utils/src/metrics/cls.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export function trackClsAsStandaloneSpan(): void {
9090
function sendStandaloneClsSpan(clsValue: number, entry: LayoutShift | undefined, pageloadSpanId: string) {
9191
DEBUG_BUILD && logger.log(`Sending CLS span (${clsValue})`);
9292

93-
const startTime = msToSec((browserPerformanceTimeOrigin || 0) + (entry?.startTime || 0));
93+
const startTime = msToSec((browserPerformanceTimeOrigin() || 0) + (entry?.startTime || 0));
9494
const routeName = getCurrentScope().getScopeData().transactionName;
9595

9696
const name = entry ? htmlTreeAsString(entry.sources[0]?.node) : 'Layout shift';

packages/browser-utils/src/metrics/inp.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const INTERACTIONS_SPAN_MAP = new Map<number, Span>();
2828
*/
2929
export function startTrackingINP(): () => void {
3030
const performance = getBrowserPerformanceAPI();
31-
if (performance && browserPerformanceTimeOrigin) {
31+
if (performance && browserPerformanceTimeOrigin()) {
3232
const inpCallback = _trackINP();
3333

3434
return (): void => {
@@ -85,7 +85,7 @@ function _trackINP(): () => void {
8585
const interactionType = INP_ENTRY_MAP[entry.name];
8686

8787
/** Build the INP span, create an envelope from the span, and then send the envelope */
88-
const startTime = msToSec((browserPerformanceTimeOrigin as number) + entry.startTime);
88+
const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);
8989
const duration = msToSec(metric.value);
9090
const activeSpan = getActiveSpan();
9191
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;

packages/browser/src/profiling/utils.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ export function convertJSSelfProfileToSampledFormat(input: JSSelfProfile): Profi
241241
// when that happens, we need to ensure we are correcting the profile timings so the two timelines stay in sync.
242242
// Since JS self profiling time origin is always initialized to performance.timeOrigin, we need to adjust for
243243
// the drift between the SDK selected value and our profile time origin.
244-
const origin =
245-
typeof performance.timeOrigin === 'number' ? performance.timeOrigin : browserPerformanceTimeOrigin || 0;
246-
const adjustForOriginChange = origin - (browserPerformanceTimeOrigin || origin);
244+
const perfOrigin = browserPerformanceTimeOrigin();
245+
const origin = typeof performance.timeOrigin === 'number' ? performance.timeOrigin : perfOrigin || 0;
246+
const adjustForOriginChange = origin - (perfOrigin || origin);
247247

248248
input.samples.forEach((jsSample, i) => {
249249
// If sample has no stack, add an empty sample

packages/browser/src/tracing/browserTracingIntegration.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,11 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
364364

365365
if (WINDOW.location) {
366366
if (instrumentPageLoad) {
367+
const origin = browserPerformanceTimeOrigin();
367368
startBrowserTracingPageLoadSpan(client, {
368369
name: WINDOW.location.pathname,
369370
// pageload should always start at timeOrigin (and needs to be in s, not ms)
370-
startTime: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
371+
startTime: origin ? origin / 1000 : undefined,
371372
attributes: {
372373
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
373374
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',

packages/browser/src/tracing/request.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ export function extractNetworkProtocol(nextHopProtocol: string): { name: string;
260260
}
261261

262262
function getAbsoluteTime(time: number = 0): number {
263-
return ((browserPerformanceTimeOrigin || performance.timeOrigin) + time) / 1000;
263+
return ((browserPerformanceTimeOrigin() || performance.timeOrigin) + time) / 1000;
264264
}
265265

266266
function resourceTimingEntryToSpanData(resourceTiming: PerformanceResourceTiming): [string, string | number][] {
@@ -270,7 +270,7 @@ function resourceTimingEntryToSpanData(resourceTiming: PerformanceResourceTiming
270270

271271
timingSpanData.push(['network.protocol.version', version], ['network.protocol.name', name]);
272272

273-
if (!browserPerformanceTimeOrigin) {
273+
if (!browserPerformanceTimeOrigin()) {
274274
return timingSpanData;
275275
}
276276
return [

packages/core/src/utils-hoist/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ export {
8888
} from './supports';
8989
export { SyncPromise, rejectedSyncPromise, resolvedSyncPromise } from './syncpromise';
9090
export {
91-
// eslint-disable-next-line deprecation/deprecation
92-
_browserPerformanceTimeOriginMode,
9391
browserPerformanceTimeOrigin,
9492
dateTimestampInSeconds,
9593
timestampInSeconds,

packages/core/src/utils-hoist/time.ts

+21-20
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,21 @@ function createUnixTimestampInSecondsFunc(): () => number {
6767
export const timestampInSeconds = createUnixTimestampInSecondsFunc();
6868

6969
/**
70-
* Internal helper to store what is the source of browserPerformanceTimeOrigin below. For debugging only.
71-
*
72-
* @deprecated This variable will be removed in the next major version.
70+
* Cached result of getBrowserTimeOrigin.
7371
*/
74-
export let _browserPerformanceTimeOriginMode: string;
72+
let cachedTimeOrigin: [number | undefined, string] | undefined;
7573

7674
/**
77-
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
78-
* performance API is available.
75+
* Gets the time origin and the mode used to determine it.
7976
*/
80-
export const browserPerformanceTimeOrigin = ((): number | undefined => {
77+
function getBrowserTimeOrigin(): [number | undefined, string] {
8178
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
8279
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
8380
// data as reliable if they are within a reasonable threshold of the current time.
8481

8582
const { performance } = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;
8683
if (!performance?.now) {
87-
// eslint-disable-next-line deprecation/deprecation
88-
_browserPerformanceTimeOriginMode = 'none';
89-
return undefined;
84+
return [undefined, 'none'];
9085
}
9186

9287
const threshold = 3600 * 1000;
@@ -114,18 +109,24 @@ export const browserPerformanceTimeOrigin = ((): number | undefined => {
114109
if (timeOriginIsReliable || navigationStartIsReliable) {
115110
// Use the more reliable time origin
116111
if (timeOriginDelta <= navigationStartDelta) {
117-
// eslint-disable-next-line deprecation/deprecation
118-
_browserPerformanceTimeOriginMode = 'timeOrigin';
119-
return performance.timeOrigin;
112+
return [performance.timeOrigin, 'timeOrigin'];
120113
} else {
121-
// eslint-disable-next-line deprecation/deprecation
122-
_browserPerformanceTimeOriginMode = 'navigationStart';
123-
return navigationStart;
114+
return [navigationStart, 'navigationStart'];
124115
}
125116
}
126117

127118
// Either both timeOrigin and navigationStart are skewed or neither is available, fallback to Date.
128-
// eslint-disable-next-line deprecation/deprecation
129-
_browserPerformanceTimeOriginMode = 'dateNow';
130-
return dateNow;
131-
})();
119+
return [dateNow, 'dateNow'];
120+
}
121+
122+
/**
123+
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
124+
* performance API is available.
125+
*/
126+
export function browserPerformanceTimeOrigin(): number | undefined {
127+
if (!cachedTimeOrigin) {
128+
cachedTimeOrigin = getBrowserTimeOrigin();
129+
}
130+
131+
return cachedTimeOrigin[0];
132+
}

packages/ember/addon/instance-initializers/sentry-performance.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,9 @@ function _instrumentInitialLoad(config: EmberSentryConfig): void {
366366
return;
367367
}
368368

369+
const origin = browserPerformanceTimeOrigin();
369370
// Split performance check in two so clearMarks still happens even if timeOrigin isn't available.
370-
if (!HAS_PERFORMANCE_TIMING || browserPerformanceTimeOrigin === undefined) {
371+
if (!HAS_PERFORMANCE_TIMING || origin === undefined) {
371372
return;
372373
}
373374
const measureName = '@sentry/ember:initial-load';
@@ -383,7 +384,7 @@ function _instrumentInitialLoad(config: EmberSentryConfig): void {
383384
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
384385
const measure = measures[0]!;
385386

386-
const startTime = (measure.startTime + browserPerformanceTimeOrigin) / 1000;
387+
const startTime = (measure.startTime + origin) / 1000;
387388
const endTime = startTime + measure.duration / 1000;
388389

389390
startInactiveSpan({

packages/nextjs/src/client/routing/appRouterRoutingInstrumentation.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ export const INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME = 'incomplet
1111

1212
/** Instruments the Next.js app router for pageloads. */
1313
export function appRouterInstrumentPageLoad(client: Client): void {
14+
const origin = browserPerformanceTimeOrigin();
1415
startBrowserTracingPageLoadSpan(client, {
1516
name: WINDOW.location.pathname,
1617
// pageload should always start at timeOrigin (and needs to be in s, not ms)
17-
startTime: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
18+
startTime: origin ? origin / 1000 : undefined,
1819
attributes: {
1920
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
2021
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.nextjs.app_router_instrumentation',

packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,13 @@ export function pagesRouterInstrumentPageLoad(client: Client): void {
119119
name = name.replace(/^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|TRACE|CONNECT)\s+/i, '');
120120
}
121121

122+
const origin = browserPerformanceTimeOrigin();
122123
startBrowserTracingPageLoadSpan(
123124
client,
124125
{
125126
name,
126127
// pageload should always start at timeOrigin (and needs to be in s, not ms)
127-
startTime: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
128+
startTime: origin ? origin / 1000 : undefined,
128129
attributes: {
129130
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
130131
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.nextjs.pages_router_instrumentation',

packages/replay-internal/src/util/createPerformanceEntries.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function createPerformanceEntry(entry: AllPerformanceEntry): ReplayPerformanceEn
8989
function getAbsoluteTime(time: number): number {
9090
// browserPerformanceTimeOrigin can be undefined if `performance` or
9191
// `performance.now` doesn't exist, but this is already checked by this integration
92-
return ((browserPerformanceTimeOrigin || WINDOW.performance.timeOrigin) + time) / 1000;
92+
return ((browserPerformanceTimeOrigin() || WINDOW.performance.timeOrigin) + time) / 1000;
9393
}
9494

9595
function createPaintEntry(entry: PerformancePaintTiming): ReplayPerformanceEntry<PaintData> {

packages/replay-internal/test/integration/flush.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('Integration | flush', () => {
9393
mockEventBufferFinish.mockClear();
9494

9595
Object.defineProperty(SentryUtils, 'browserPerformanceTimeOrigin', {
96-
value: BASE_TIMESTAMP,
96+
value: () => BASE_TIMESTAMP,
9797
writable: true,
9898
});
9999
});
@@ -107,7 +107,7 @@ describe('Integration | flush', () => {
107107
writable: true,
108108
});
109109
Object.defineProperty(SentryUtils, 'browserPerformanceTimeOrigin', {
110-
value: prevBrowserPerformanceTimeOrigin,
110+
value: () => prevBrowserPerformanceTimeOrigin,
111111
writable: true,
112112
});
113113
});

packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ vi.setSystemTime(new Date('2023-01-01'));
77

88
vi.mock('@sentry/core', async () => ({
99
...(await vi.importActual('@sentry/core')),
10-
browserPerformanceTimeOrigin: new Date('2023-01-01').getTime(),
10+
browserPerformanceTimeOrigin: () => new Date('2023-01-01').getTime(),
1111
}));
1212

1313
import { WINDOW } from '../../../src/constants';

0 commit comments

Comments
 (0)