-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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]: Use Performance API #2474
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kamil recently added some fallback code to support Node 6 and whatever else don't have the performance API, we may want to integrate with that -- even if it means explicitly disabling some functionality depending on the availability of the API.
This integration only works in browsers so it's not relevant I think. |
// We go through all scripts on the page and look for 'data-entry' | ||
// We remember the name and measure the time between this script finished loading and | ||
// our mark 'sentry-tracing-init' | ||
if (document.scripts[i].getAttribute('data-entry') === 'true') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the user need to manually indicate the data-entry
attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, otherwise it's hard to determine which is the "entry script" of your application.
If that data-entry
is not there, it's just missing the script.evaluation
span.
Also added an internal note in the description of the PR so we don't forget to document this if we think it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's unit test the span update logic to avoid breaking it when we touch this code in the future?
performance.clearMarks(); | ||
performance.clearMeasures(); | ||
performance.clearResourceTimings(); | ||
Tracing._performanceCursor = Math.max(performance.getEntries().length - 1, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the intent of this _performanceCursor
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rhcarvalho From what I understand, it's to skip over certain entries that were created after clearing the performance entries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, since not all of the performance entries can be cleared with clear*()
we need to remember which entries we already considered. And we call clear in the first place since browsers have an upper limit so this can overflow. TBH I am not sure if clearing this is a good practice but we'll see.
Tracing._performanceCursor = Math.max(performance.getEntries().length - 1, 0); | ||
} | ||
|
||
// tslint:enable: no-unsafe-any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary here?!
*/ | ||
private static _addOffsetToSpan(measureName: string, span: SpanClass): void { | ||
if (global.performance) { | ||
const name = `#sentry-${measureName}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the #
in front of sentry
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to make it "private" :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a global namespace (within the perf entries), #sentry-foo
is no more private than sentry-foo
, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. Likely nobody should use this prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to use a "special char" in order if we ever need to do any string matching. No other reason than my personal style to prefix it with #
^^
if (global.performance) { | ||
const name = `#sentry-${measureName}`; | ||
performance.measure(name); | ||
const measure = performance.getEntriesByName(name).pop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can be more than one entry with the same name!
In this very page here, open the developer console and run:
> performance.getEntriesByName("https://github.com/getsentry/sentry-javascript/pull/2474/review_comment/create")
(8) [PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming, PerformanceResourceTiming]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true, but since this mark /measure is only so short, it only lives to calculate the offset.
Therefore I think this can never return more than one.
private static _msToSec(time: number): number { | ||
return time / 1000; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd imagine we must already have this somewhere else? Though I prefer repeating it here than adding a dependency on a leftpad :P
@rhcarvalho It looks like the code paths with these changes will only be run in browsers. For node.js, there are specialized performance instrumentation: https://nodejs.org/api/perf_hooks.html#perf_hooks_performanceentry_entrytype I imagine that could be supported later on. |
* Also, we update our timings since we consider the timings in this API to be more correct than our manual | ||
* measurements. | ||
* | ||
* @param transactionSpan The transaction span |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revolver ocelot
— revolver ocelot
) { | ||
navigationOffset = transactionSpan.data.offset; | ||
} | ||
// tslint:disable-next-line: completed-docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can just disable it in a whole file instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem seems to be TS lint here, even if I write docs it still complains.
op: 'browser', | ||
}); | ||
// tslint:disable: no-unsafe-any | ||
span.startTimestamp = parent.startTimestamp + Tracing._msToSec(entry[`${event}Start`] as number); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be written as
span.timestamp = span.startTimestamp = parent.startTimestamp + Tracing._msToSec(entry[`${event}Start`] as number);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope,
Tracing._msToSec(entry[`${event}Start`]
Tracing._msToSec(entry[`${event}End`]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
Co-Authored-By: Rodolfo Carvalho <[email protected]>
…ascript into apm-performance-api
If available we use https://developer.mozilla.org/en-US/docs/Web/API/Performance API to get more insight into page load. This is especially helpful for the initial
pageLoad
since we get a lot of additional info there.In addition to new spans, we also use the timings of the performance API to update our measurements.
Before:
After:
Internal note:
We need to document that if users mark their entry script with
data-entry="true"
like:<script src="..." data-entry="true"></script>
We are able to measure the script evaluation.
ref: https://github.com/getsentry/sentry/pull/17501/files#diff-cd6de428a381ccfe274524ae3f0e9ea1L41