-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
Inject Lighthouse metrics back into trace for viewing #1446
Conversation
c1ff959
to
9de452e
Compare
Nice API 😍 , it will make |
this would be so awesome! 🎉 |
@@ -0,0 +1,204 @@ | |||
/** | |||
* @license | |||
* Copyright 2016 Google Inc. All rights reserved. |
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.
2017
|
||
const log = require('../../../lighthouse-core/lib/log.js'); | ||
|
||
class Metrics { |
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.
throw in a @fileoverview
name: 'First Contentful Paint', | ||
id: 'ttfcp', | ||
getTs: auditResults => { | ||
const fmpExt = auditResults['first-meaningful-paint'].extendedInfo; |
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.
Everything coming from auditResults['first-meaningful-paint']
looks like a mistake. Do we stash several things in auditResults['first-meaningful-paint'].extendedInfo
? There are a few others like that, but reading this code is non-obvious.
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.
Essentially we have 3 actual audits in lighthouse that collect these metrics. And yeah we stash several key metrics within the extInfo of each of them.
So, yeah, AFAIK all this is correct and intended... any ideas on how it could look less odd?
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.
was expecting all the first-*
stuff to be together, but I guess that makes sense if they're coming from different audits.
name: 'Visually Complete 85%', | ||
id: 'vc85', | ||
getTs: auditResults => { | ||
const siExt = auditResults['time-to-interactive'].extendedInfo; |
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.
auditResults['speed-index-metric']
?
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.
Actually, check that all of these are right. Now I'm really confused :)
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.
actually vc85 does actually get delivered on the TTI choo-choo train. :)
that does mean that siExt
=> ttiExt
, so thanks for catching that!
}, | ||
{ | ||
name: 'Visually Complete 100%', | ||
id: 'vc', |
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.
suggest : id: 'vc100'
to be like the other
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.
sounds good.
I had chosen 'vc' origianlly because WebPageTest uses vc
for the same thing. OR SO I THOUGHT!. Looking again... i can't see "vc" anywhere, so yeah i'm happy to tweak the id.
const metricDfns = Metrics.metricsDefinitions; | ||
const resolvedMetrics = []; | ||
metricDfns.forEach(metric => { | ||
// try/catch in case auditResults is missing an audit result |
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.
an -> in
|
||
|
||
/** | ||
* Constructs performance.measure trace events, which have start/end events as follows: |
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 "ph: b", beginning? If so, maybe say being/end events
instead of start/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.
actually "b" stands for "nestable start" :D
The trace event phases are kind of insane.
@@ -0,0 +1,85 @@ | |||
/** | |||
* @license | |||
* Copyright 2016 Google Inc. All rights reserved. |
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.
2017
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.
reviewed!
const assert = require('assert'); | ||
|
||
const dbwTrace = require('../../fixtures/traces/dbw_tester.json'); | ||
const dbwResults = require('../../fixtures/dbw_tester-perf-results.json'); |
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.
can we reuse an existing trace? We're already at 26MB of trace fixtures and growing :)
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.
was hoping you'd ask that. :) In this case it's pretty tricky because we need mock results and mock trace from the exact same run. Currently we have no fixtures that satisfy that.
I attempted to update one of our sample.json
's (yes we have two) but after 25 minutes i concluded the rabbit hole was a bit too deep for me. :/
}); | ||
}); | ||
|
||
describe('assetSaver prepareAssets', () => { |
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.
should probably be in asset-saver-test.js
@@ -0,0 +1,204 @@ | |||
/** |
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.
rename to indicate progressive web metrics? pwmetrics-events.js
? Or is the intention to keep expanding?
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.
renamed. sg
* @param {!Array<!AuditResult>} auditResults | ||
* @return {!Array<{ts: number, id: string, name: string}>} metrics to consider | ||
*/ | ||
gatherMetrics(auditResults) { |
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.
make static? (or can use this._auditResults
)
/** | ||
* Getter for our navigationStart trace event | ||
*/ | ||
get navigationStartEvt() { |
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.
make just getNavigationStartEvt()
or something instead of a getter? Kind of confusing below when this property appears.
Could also be an internal function that the constructor calls, populating the this.navigationStartEvt
property so the behavior doesn't change from this
evts = new Metrics(dbwTrace.traceEvents, dbwResults.audits).generateFakeEvents(); | ||
|
||
const metricsWithoutNavstart = Metrics.metricsDefinitions.length - 1; | ||
assert.equal(evts.length, 2 * metricsWithoutNavstart, 'All expected fake events not created'); |
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 a comment that the events come in pairs, hence the 2 *
?
const definitionsWithoutEvents = Metrics.metricsDefinitions | ||
.filter(metric => metric.id !== 'navstart') | ||
.filter(metric => !evts.find(e => e.name === metric.name)); | ||
assert.deepEqual(definitionsWithoutEvents, [], 'metrics are missing fake events'); |
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.
better to do assert.strictEqual(definitionsWithoutEvents.length, 0, 'metrics are missing fake events');
?
|
||
const eventsWithoutDefinitions = evts | ||
.filter(evt => !Metrics.metricsDefinitions.find(metric => metric.name === evt.name)); | ||
assert.deepEqual(eventsWithoutDefinitions, [], 'fake events without a metric definition'); |
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.
same length thing
}); | ||
|
||
it('generates fake trace events that are valid', () => { | ||
const vizCompleteEvts = evts.filter(e => e.name.includes('Visually Complete 100')); |
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.
ideally we could avoid dependence on test ordering like this (I'd like to run our tests in a random order at some point, though we do this sort of thing in a few places in current tests :). Just re-run const evts = new Metrics(dbwTrace.traceEvents, dbwResults.audits).generateFakeEvents()
? This test doesn't trigger the > 50ms warning on Travis, so should still be fast.
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.
sgtm
@@ -119,6 +120,10 @@ function prepareAssets(artifacts, results) { | |||
const traceData = Object.assign({}, trace); | |||
const html = screenshotDump(screenshots, results); | |||
|
|||
if (results && results.audits) { | |||
const evts = new Metrics(traceData.traceEvents, results.audits).generateFakeEvents(); | |||
traceData.traceEvents.push(...evts); |
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 seems bad to alter the original trace. Not sure when we did const traceData = Object.assign({}, trace);
, but really we should be doing something like traceData.traceEvents = Array.from(traceData.traceEvents)
to get a new array. Is that crazy?
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.
On one hand, yeah, it feels bad to alter the trace. It'd be nice to keep the objects separate.
On the other:
- I don't think we have a need to keep around the original trace.
- We've sorted the trace in-place already (a few times).
- The memory consumption is pretty huge for these traces, so there's that advantage to augmenting the solo trace.
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.
We've sorted the trace in-place already (a few times).
very good point :)
@paulirish do you need some help for finishing this? |
864f2cc
to
b7aefd6
Compare
PTAL |
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.
thanks guys 🎉 |
Fixes #616
Here's a preview of what this patch gives us:
![image](https://cloud.githubusercontent.com/assets/39191/21796487/f35ad136-d6bd-11e6-9447-2260adcf1d65.png)
Live example: https://chromedevtools.github.io/timeline-viewer/?loadTimelineFromURL=https://www.dropbox.com/s/wqzll3jjbdseooe/hypem.com_01-05-2017_7-33-42_PM-0.trace.json?dl=0
In short: the lighthouse metrics like "First meaningful paint", "Time to Interactive", etc are mocked out as User Timing measures, such that they can be viewed in the devtools timeline. The these user timing measures are added right to the trace, so if you saved it with
--save-assets
, yourhostname.datetime.trace.json
file has the good stuff.Additionally, this opens up other tools like pwmetrics to consume the metrics easier without so much reinterpretation of the audit results. cc @denar90