Skip to content

Commit 13d8398

Browse files
fix: Use monotonic clock to compute durations (#2441)
A monotonic clock is monotonically increasing and not subject to system clock adjustments or system clock skew. The difference between any two chronologically recorded time values returned from the Performance.now() method MUST never be negative if the two time values have the same time origin. The same guarantee above does not exist for the difference between two calls to `new Date().getTime()` as used by `timestampWithMs()`. Resources: https://stackoverflow.com/questions/7272395/monotonically-increasing-time-in-javascript https://caniuse.com/#search=performance.now https://www.w3.org/TR/hr-time/#sec-monotonic-clock Co-authored-by: Kamil Ogórek <[email protected]>
1 parent 2e11882 commit 13d8398

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

packages/apm/src/span.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,26 @@
22

33
import { getCurrentHub, Hub } from '@sentry/hub';
44
import { Span as SpanInterface, SpanContext, SpanStatus } from '@sentry/types';
5-
import { dropUndefinedKeys, isInstanceOf, logger, timestampWithMs, uuid4 } from '@sentry/utils';
5+
import {
6+
dropUndefinedKeys,
7+
dynamicRequire,
8+
getGlobalObject,
9+
isInstanceOf,
10+
isNodeEnv,
11+
logger,
12+
timestampWithMs,
13+
uuid4,
14+
} from '@sentry/utils';
15+
16+
const global = getGlobalObject<Window>();
17+
18+
const performanceNow = (() => {
19+
if (isNodeEnv()) {
20+
const { performance } = dynamicRequire(module, 'perf_hooks');
21+
return performance.now;
22+
}
23+
return global.performance.now.bind(global.performance);
24+
})();
625

726
// TODO: Should this be exported?
827
export const TRACEPARENT_REGEXP = new RegExp(
@@ -81,6 +100,17 @@ export class Span implements SpanInterface, SpanContext {
81100
*/
82101
public readonly startTimestamp: number = timestampWithMs();
83102

103+
/**
104+
* Internal start time tracked with a monotonic clock.
105+
*
106+
* Works with mostly any browser version released since 2012.
107+
* https://caniuse.com/#search=performance.now
108+
*
109+
* Works with Node.js v8.5.0 or higher.
110+
* https://nodejs.org/api/perf_hooks.html#perf_hooks_performance_now
111+
*/
112+
private readonly _startTimestampMonotonic: number = performanceNow();
113+
84114
/**
85115
* Finish timestamp of the span.
86116
*/
@@ -261,7 +291,8 @@ export class Span implements SpanInterface, SpanContext {
261291
return undefined;
262292
}
263293

264-
this.timestamp = timestampWithMs();
294+
const durationSeconds = (performanceNow() - this._startTimestampMonotonic) / 1000;
295+
this.timestamp = this.startTimestamp + durationSeconds;
265296

266297
if (this.spanRecorder === undefined) {
267298
return undefined;

0 commit comments

Comments
 (0)