Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apm): Update Span timing from absolute ref #2479

Merged
merged 1 commit into from
Mar 9, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 37 additions & 21 deletions packages/apm/src/integrations/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ interface Activity {
const global = getGlobalObject<Window>();
const defaultTracingOrigins = ['localhost', /^\//];

if (global.performance) {
// Polyfill for performance.timeOrigin.
//
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
// tslint:disable-next-line:strict-type-predicates
if (performance.timeOrigin === undefined) {
// @ts-ignore
// tslint:disable-next-line:deprecation
performance.timeOrigin = performance.timing.navigationStart;
}
}

/**
* Tracing Integration
*/
Expand Down Expand Up @@ -382,15 +395,11 @@ export class Tracing implements Integration {
// Gatekeeper if performance API not available
return;
}

logger.log('[Tracing] Adding & adjusting spans using Performance API');
let navigationOffset = 0;
if (
transactionSpan.op === 'navigation' &&
transactionSpan.data &&
typeof transactionSpan.data.offset === 'number'
) {
navigationOffset = transactionSpan.data.offset;
}

const timeOrigin = Tracing._msToSec(performance.timeOrigin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhcarvalho I came across this polyfill to replace an older Chrome API: https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated#startloadtime

wondering if we could make use of it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Let's open the can of worms.
Need to make sure whether the times in entries are relative to timeOrigin or navigationStart, if the two ever differ -- @HazAT and I have only seen performance.getEntriesByType('navigation')[0].startTime === 0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This potential update could be something that can be in a new PR; not in this PR 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/startTime

The startTime property returns the first recorded timestamp of the performance entry.

The value returned by this property depends on the performance entry's type:
...
"navigation" - returns the timestamp with a value of "0".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w3c/navigation-timing#43

Seems that the timeOrigin and navigationStart should be the same in practice.

In that case, startloadtime is unnecessarily verbose, giving us nothing more than what we have now? @dashed wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhcarvalho that makes sense. I didn't realize navigation is essentially 0. Thank you for investigating this further 👍


// tslint:disable-next-line: completed-docs
function addSpan(span: SpanClass): void {
if (transactionSpan.spanRecorder) {
Expand All @@ -404,8 +413,8 @@ export class Tracing implements Integration {
description: event,
op: 'browser',
});
span.startTimestamp = parent.startTimestamp + Tracing._msToSec(entry[`${event}Start`]);
span.timestamp = parent.startTimestamp + Tracing._msToSec(entry[`${event}End`]);
span.startTimestamp = timeOrigin + Tracing._msToSec(entry[`${event}Start`]);
span.timestamp = timeOrigin + Tracing._msToSec(entry[`${event}End`]);
addSpan(span);
}

Expand All @@ -415,15 +424,15 @@ export class Tracing implements Integration {
description: 'request',
op: 'browser',
});
request.startTimestamp = parent.startTimestamp + Tracing._msToSec(entry.requestStart);
request.timestamp = parent.startTimestamp + Tracing._msToSec(entry.responseEnd);
request.startTimestamp = timeOrigin + Tracing._msToSec(entry.requestStart);
request.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);
addSpan(request);
const response = parent.child({
description: 'response',
op: 'browser',
});
response.startTimestamp = parent.startTimestamp + Tracing._msToSec(entry.responseStart);
response.timestamp = parent.startTimestamp + Tracing._msToSec(entry.responseEnd);
response.startTimestamp = timeOrigin + Tracing._msToSec(entry.responseStart);
response.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);
addSpan(response);
}

Expand Down Expand Up @@ -469,7 +478,7 @@ export class Tracing implements Integration {
description: `${entry.entryType} ${entry.name}`,
op: 'mark',
});
mark.startTimestamp = transactionSpan.startTimestamp + startTime - navigationOffset;
mark.startTimestamp = timeOrigin + startTime;
mark.timestamp = mark.startTimestamp + duration;
if (tracingInitMarkStartTime === undefined && entry.name === 'sentry-tracing-init') {
tracingInitMarkStartTime = mark.startTimestamp;
Expand All @@ -483,7 +492,7 @@ export class Tracing implements Integration {
if (transactionSpan.spanRecorder) {
transactionSpan.spanRecorder.finishedSpans.map((finishedSpan: SpanClass) => {
if (finishedSpan.description && finishedSpan.description.indexOf(resourceName) !== -1) {
finishedSpan.startTimestamp = transactionSpan.startTimestamp + startTime - navigationOffset;
finishedSpan.startTimestamp = timeOrigin + startTime;
finishedSpan.timestamp = finishedSpan.startTimestamp + duration;
}
});
Expand All @@ -493,7 +502,7 @@ export class Tracing implements Integration {
description: `${entry.initiatorType} ${resourceName}`,
op: `resource`,
});
resource.startTimestamp = transactionSpan.startTimestamp + startTime - navigationOffset;
resource.startTimestamp = timeOrigin + startTime;
resource.timestamp = resource.startTimestamp + duration;
// We remember the entry script end time to calculate the difference to the first init mark
if (entryScriptStartEndTime === undefined && (entryScriptSrc || '').includes(resourceName)) {
Expand Down Expand Up @@ -649,10 +658,17 @@ export class Tracing implements Integration {
});
}
span.finish();
// If there is an offset in data, we need to shift timestamps towards it
if (span.data && typeof span.data.offset === 'number' && typeof span.timestamp === 'number') {
span.startTimestamp += span.data.offset;
span.timestamp += span.data.offset;
// If there is an offset in data, update timestamps accordingly
if (
global.performance &&
span.data &&
typeof span.data.offset === 'number' &&
typeof span.timestamp === 'number'
) {
const timeOrigin = Tracing._msToSec(performance.timeOrigin);
const duration = span.timestamp - span.startTimestamp;
span.startTimestamp = timeOrigin + span.data.offset;
span.timestamp = timeOrigin + duration;
}
}
// tslint:disable-next-line: no-dynamic-delete
Expand Down