1
- import { dynamicRequire , isNodeEnv } from './node' ;
2
- import { getGlobalObject } from './worldwide' ;
1
+ import { GLOBAL_OBJ } from './worldwide' ;
3
2
4
- // eslint-disable-next-line deprecation/deprecation
5
- const WINDOW = getGlobalObject < Window > ( ) ;
6
-
7
- /**
8
- * An object that can return the current timestamp in seconds since the UNIX epoch.
9
- */
10
- interface TimestampSource {
11
- nowSeconds ( ) : number ;
12
- }
13
-
14
- /**
15
- * A TimestampSource implementation for environments that do not support the Performance Web API natively.
16
- *
17
- * Note that this TimestampSource does not use a monotonic clock. A call to `nowSeconds` may return a timestamp earlier
18
- * than a previously returned value. We do not try to emulate a monotonic behavior in order to facilitate debugging. It
19
- * is more obvious to explain "why does my span have negative duration" than "why my spans have zero duration".
20
- */
21
- const dateTimestampSource : TimestampSource = {
22
- nowSeconds : ( ) => Date . now ( ) / 1000 ,
23
- } ;
3
+ const ONE_SECOND_IN_MS = 1000 ;
24
4
25
5
/**
26
6
* A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
@@ -37,89 +17,56 @@ interface Performance {
37
17
now ( ) : number ;
38
18
}
39
19
20
+ /**
21
+ * Returns a timestamp in seconds since the UNIX epoch using the Date API.
22
+ *
23
+ * TODO(v8): Return type should be rounded.
24
+ */
25
+ export function dateTimestampInSeconds ( ) : number {
26
+ return Date . now ( ) / ONE_SECOND_IN_MS ;
27
+ }
28
+
40
29
/**
41
30
* Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
42
31
* support the API.
43
32
*
44
33
* Wrapping the native API works around differences in behavior from different browsers.
45
34
*/
46
- function getBrowserPerformance ( ) : Performance | undefined {
47
- const { performance } = WINDOW ;
35
+ function createUnixTimestampInSecondsFunc ( ) : ( ) => number {
36
+ const { performance } = GLOBAL_OBJ as typeof GLOBAL_OBJ & { performance ?: Performance } ;
48
37
if ( ! performance || ! performance . now ) {
49
- return undefined ;
38
+ return dateTimestampInSeconds ;
50
39
}
51
40
52
- // Replace performance.timeOrigin with our own timeOrigin based on Date.now().
53
- //
54
- // This is a partial workaround for browsers reporting performance.timeOrigin such that performance.timeOrigin +
55
- // performance.now() gives a date arbitrarily in the past.
56
- //
57
- // Additionally, computing timeOrigin in this way fills the gap for browsers where performance.timeOrigin is
58
- // undefined.
59
- //
60
- // The assumption that performance.timeOrigin + performance.now() ~= Date.now() is flawed, but we depend on it to
61
- // interact with data coming out of performance entries.
62
- //
63
- // Note that despite recommendations against it in the spec, browsers implement the Performance API with a clock that
64
- // might stop when the computer is asleep (and perhaps under other circumstances). Such behavior causes
65
- // performance.timeOrigin + performance.now() to have an arbitrary skew over Date.now(). In laptop computers, we have
66
- // observed skews that can be as long as days, weeks or months.
67
- //
68
- // See https://github.com/getsentry/sentry-javascript/issues/2590.
41
+ // Some browser and environments don't have a timeOrigin, so we fallback to
42
+ // using Date.now() to compute the starting time.
43
+ const approxStartingTimeOrigin = Date . now ( ) - performance . now ( ) ;
44
+ const timeOrigin = performance . timeOrigin == undefined ? approxStartingTimeOrigin : performance . timeOrigin ;
45
+
46
+ // performance.now() is a monotonic clock, which means it starts at 0 when the process begins. To get the current
47
+ // wall clock time (actual UNIX timestamp), we need to add the starting time origin and the current time elapsed.
69
48
//
70
- // BUG: despite our best intentions, this workaround has its limitations. It mostly addresses timings of pageload
71
- // transactions, but ignores the skew built up over time that can aversely affect timestamps of navigation
72
- // transactions of long-lived web pages .
73
- const timeOrigin = Date . now ( ) - performance . now ( ) ;
74
-
75
- return {
76
- now : ( ) => performance . now ( ) ,
77
- timeOrigin,
49
+ // TODO: This does not account for the case where the monotonic clock that powers performance.now() drifts from the
50
+ // wall clock time, which causes the returned timestamp to be inaccurate. We should investigate how to detect and
51
+ // correct for this .
52
+ // See: https://github.com/getsentry/sentry-javascript/issues/2590
53
+ // See: https://github.com/mdn/content/issues/4713
54
+ // See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
55
+ return ( ) => {
56
+ return ( timeOrigin + performance . now ( ) ) / ONE_SECOND_IN_MS ;
78
57
} ;
79
58
}
80
59
81
- /**
82
- * Returns the native Performance API implementation from Node.js. Returns undefined in old Node.js versions that don't
83
- * implement the API.
84
- */
85
- function getNodePerformance ( ) : Performance | undefined {
86
- try {
87
- const perfHooks = dynamicRequire ( module , 'perf_hooks' ) as { performance : Performance } ;
88
- return perfHooks . performance ;
89
- } catch ( _ ) {
90
- return undefined ;
91
- }
92
- }
93
-
94
- /**
95
- * The Performance API implementation for the current platform, if available.
96
- */
97
- const platformPerformance : Performance | undefined = isNodeEnv ( ) ? getNodePerformance ( ) : getBrowserPerformance ( ) ;
98
-
99
- const timestampSource : TimestampSource =
100
- platformPerformance === undefined
101
- ? dateTimestampSource
102
- : {
103
- nowSeconds : ( ) => ( platformPerformance . timeOrigin + platformPerformance . now ( ) ) / 1000 ,
104
- } ;
105
-
106
- /**
107
- * Returns a timestamp in seconds since the UNIX epoch using the Date API.
108
- */
109
- export const dateTimestampInSeconds : ( ) => number = dateTimestampSource . nowSeconds . bind ( dateTimestampSource ) ;
110
-
111
60
/**
112
61
* Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
113
62
* availability of the Performance API.
114
63
*
115
- * See `usingPerformanceAPI` to test whether the Performance API is used.
116
- *
117
64
* BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
118
65
* asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
119
66
* skew can grow to arbitrary amounts like days, weeks or months.
120
67
* See https://github.com/getsentry/sentry-javascript/issues/2590.
121
68
*/
122
- export const timestampInSeconds : ( ) => number = timestampSource . nowSeconds . bind ( timestampSource ) ;
69
+ export const timestampInSeconds = createUnixTimestampInSecondsFunc ( ) ;
123
70
124
71
/**
125
72
* Re-exported with an old name for backwards-compatibility.
@@ -129,11 +76,6 @@ export const timestampInSeconds: () => number = timestampSource.nowSeconds.bind(
129
76
*/
130
77
export const timestampWithMs = timestampInSeconds ;
131
78
132
- /**
133
- * A boolean that is true when timestampInSeconds uses the Performance API to produce monotonic timestamps.
134
- */
135
- export const usingPerformanceAPI = platformPerformance !== undefined ;
136
-
137
79
/**
138
80
* Internal helper to store what is the source of browserPerformanceTimeOrigin below. For debugging only.
139
81
*/
@@ -148,7 +90,7 @@ export const browserPerformanceTimeOrigin = ((): number | undefined => {
148
90
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
149
91
// data as reliable if they are within a reasonable threshold of the current time.
150
92
151
- const { performance } = WINDOW ;
93
+ const { performance } = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window ;
152
94
if ( ! performance || ! performance . now ) {
153
95
_browserPerformanceTimeOriginMode = 'none' ;
154
96
return undefined ;
0 commit comments