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

core(metrics): consumable metrics audit output #5101

Merged
merged 5 commits into from
May 4, 2018
Merged
Show file tree
Hide file tree
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
134 changes: 85 additions & 49 deletions lighthouse-core/audits/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,66 +39,102 @@ class Metrics extends Audit {
const interactive = await artifacts.requestInteractive(metricComputationData);
const speedIndex = await artifacts.requestSpeedIndex(metricComputationData);
const estimatedInputLatency = await artifacts.requestEstimatedInputLatency(metricComputationData); // eslint-disable-line max-len
const metrics = [];

// Include the simulated/observed performance metrics
const metricsMap = {
firstContentfulPaint,
firstMeaningfulPaint,
firstCPUIdle,
interactive,
speedIndex,
estimatedInputLatency,
};
/** @type {UberMetricsItem} */
const metrics = {
// Include the simulated/observed performance metrics
firstContentfulPaint: firstContentfulPaint.timing,
firstContentfulPaintTs: firstContentfulPaint.timestamp,
firstMeaningfulPaint: firstMeaningfulPaint.timing,
firstMeaningfulPaintTs: firstMeaningfulPaint.timestamp,
firstCPUIdle: firstCPUIdle.timing,
firstCPUIdleTs: firstCPUIdle.timestamp,
interactive: interactive.timing,
interactiveTs: interactive.timestamp,
speedIndex: speedIndex.timing,
speedIndexTs: speedIndex.timestamp,
estimatedInputLatency: estimatedInputLatency.timing,
estimatedInputLatencyTs: estimatedInputLatency.timestamp,

for (const [metricName, values] of Object.entries(metricsMap)) {
metrics.push({
metricName,
timing: Math.round(values.timing),
timestamp: values.timestamp,
});
}
// Include all timestamps of interest from trace of tab
observedNavigationStart: traceOfTab.timings.navigationStart,
observedNavigationStartTs: traceOfTab.timestamps.navigationStart,
observedFirstPaint: traceOfTab.timings.firstPaint,
observedFirstPaintTs: traceOfTab.timestamps.firstPaint,
observedFirstContentfulPaint: traceOfTab.timings.firstContentfulPaint,
observedFirstContentfulPaintTs: traceOfTab.timestamps.firstContentfulPaint,
observedFirstMeaningfulPaint: traceOfTab.timings.firstMeaningfulPaint,
observedFirstMeaningfulPaintTs: traceOfTab.timestamps.firstMeaningfulPaint,
observedTraceEnd: traceOfTab.timings.traceEnd,
observedTraceEndTs: traceOfTab.timestamps.traceEnd,
observedLoad: traceOfTab.timings.load,
observedLoadTs: traceOfTab.timestamps.load,
observedDomContentLoaded: traceOfTab.timings.domContentLoaded,
observedDomContentLoadedTs: traceOfTab.timestamps.domContentLoaded,

// Include all timestamps of interest from trace of tab
const timingsEntries = /** @type {Array<[keyof LH.Artifacts.TraceTimes, number]>} */
(Object.entries(traceOfTab.timings));
for (const [traceEventName, timing] of timingsEntries) {
const uppercased = traceEventName.slice(0, 1).toUpperCase() + traceEventName.slice(1);
const metricName = `observed${uppercased}`;
const timestamp = traceOfTab.timestamps[traceEventName];
metrics.push({metricName, timing, timestamp});
}

// Include some visual metrics from speedline
metrics.push({
metricName: 'observedFirstVisualChange',
timing: speedline.first,
timestamp: (speedline.first + speedline.beginning) * 1000,
});
metrics.push({
metricName: 'observedLastVisualChange',
timing: speedline.complete,
timestamp: (speedline.complete + speedline.beginning) * 1000,
});
metrics.push({
metricName: 'observedSpeedIndex',
timing: speedline.speedIndex,
timestamp: (speedline.speedIndex + speedline.beginning) * 1000,
});
// Include some visual metrics from speedline
observedFirstVisualChange: speedline.first,
observedFirstVisualChangeTs: (speedline.first + speedline.beginning) * 1000,
observedLastVisualChange: speedline.complete,
observedLastVisualChangeTs: (speedline.complete + speedline.beginning) * 1000,
observedSpeedIndex: speedline.speedIndex,
observedSpeedIndexTs: (speedline.speedIndex + speedline.beginning) * 1000,
};

const headings = [
{key: 'metricName', itemType: 'text', text: 'Name'},
{key: 'timing', itemType: 'ms', granularity: 10, text: 'Value (ms)'},
];
for (const [name, value] of Object.entries(metrics)) {
const key = /** @type {keyof UberMetricsItem} */ (name);
if (typeof value !== 'undefined') {
metrics[key] = Math.round(value);
}
}

const tableDetails = Audit.makeTableDetails(headings, metrics);
/** @type {MetricsDetails} */
const details = {items: [metrics]};

return {
score: 1,
rawValue: interactive.timing,
details: tableDetails,
details,
};
}
}

/**
* @typedef UberMetricsItem
* @property {number} firstContentfulPaint
* @property {number=} firstContentfulPaintTs
* @property {number} firstMeaningfulPaint
* @property {number=} firstMeaningfulPaintTs
* @property {number} firstCPUIdle
* @property {number=} firstCPUIdleTs
* @property {number} interactive
* @property {number=} interactiveTs
* @property {number} speedIndex
* @property {number=} speedIndexTs
* @property {number} estimatedInputLatency
* @property {number=} estimatedInputLatencyTs
* @property {number} observedNavigationStart
* @property {number} observedNavigationStartTs
* @property {number} observedFirstPaint
* @property {number} observedFirstPaintTs
* @property {number} observedFirstContentfulPaint
* @property {number} observedFirstContentfulPaintTs
* @property {number} observedFirstMeaningfulPaint
* @property {number} observedFirstMeaningfulPaintTs
* @property {number} observedTraceEnd
* @property {number} observedTraceEndTs
* @property {number} observedLoad
* @property {number} observedLoadTs
* @property {number} observedDomContentLoaded
* @property {number} observedDomContentLoadedTs
* @property {number} observedFirstVisualChange
* @property {number} observedFirstVisualChangeTs
* @property {number} observedLastVisualChange
* @property {number} observedLastVisualChangeTs
* @property {number} observedSpeedIndex
* @property {number} observedSpeedIndexTs
*/

/** @typedef {{items: [UberMetricsItem]}} MetricsDetails */

module.exports = Metrics;
52 changes: 27 additions & 25 deletions lighthouse-core/lib/traces/pwmetrics-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

const log = require('lighthouse-logger');

function findValueInMetricsAuditFn(metricName, timingOrTimestamp) {
// TODO: rework this file to not need this function
// see https://github.com/GoogleChrome/lighthouse/pull/5101/files#r186168840
function findValueInMetricsAuditFn(metricName) {
return auditResults => {
const metricsAudit = auditResults.metrics;
if (!metricsAudit || !metricsAudit.details || !metricsAudit.details.items) return;

const values = metricsAudit.details.items.find(item => item.metricName === metricName);
return values && values[timingOrTimestamp];
const values = metricsAudit.details.items[0];
return values && values[metricName];
Copy link
Member

Choose a reason for hiding this comment

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

this can be a TODO, but seems like we can just get rid of this whole findValueInMetricsAuditFn thing now. If there's no metrics audit, pwmetrics can't really do much, so it could check once at the beginning. Then all the gets are just property dereferencing with the new object layout.

(metricsDefinitions might still be useful, haven't looked into it)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sure TODO added

};
}

Expand All @@ -33,68 +35,68 @@ class Metrics {
{
name: 'Navigation Start',
id: 'navstart',
getTs: findValueInMetricsAuditFn('observedNavigationStart', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedNavigationStart', 'timing'),
getTs: findValueInMetricsAuditFn('observedNavigationStartTs'),
getTiming: findValueInMetricsAuditFn('observedNavigationStart'),
},
{
name: 'First Contentful Paint',
id: 'ttfcp',
getTs: findValueInMetricsAuditFn('observedFirstContentfulPaint', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedFirstContentfulPaint', 'timing'),
getTs: findValueInMetricsAuditFn('observedFirstContentfulPaintTs'),
getTiming: findValueInMetricsAuditFn('observedFirstContentfulPaint'),
},
{
name: 'First Meaningful Paint',
id: 'ttfmp',
getTs: findValueInMetricsAuditFn('observedFirstMeaningfulPaint', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedFirstMeaningfulPaint', 'timing'),
getTs: findValueInMetricsAuditFn('observedFirstMeaningfulPaintTs'),
getTiming: findValueInMetricsAuditFn('observedFirstMeaningfulPaint'),
},
{
name: 'Speed Index',
id: 'si',
getTs: findValueInMetricsAuditFn('observedSpeedIndex', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedSpeedIndex', 'timing'),
getTs: findValueInMetricsAuditFn('observedSpeedIndexTs'),
getTiming: findValueInMetricsAuditFn('observedSpeedIndex'),
},
{
name: 'First Visual Change',
id: 'fv',
getTs: findValueInMetricsAuditFn('observedFirstVisualChange', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedFirstVisualChange', 'timing'),
getTs: findValueInMetricsAuditFn('observedFirstVisualChangeTs'),
getTiming: findValueInMetricsAuditFn('observedFirstVisualChange'),
},
{
name: 'Visually Complete 100%',
id: 'vc100',
getTs: findValueInMetricsAuditFn('observedLastVisualChange', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedLastVisualChange', 'timing'),
getTs: findValueInMetricsAuditFn('observedLastVisualChangeTs'),
getTiming: findValueInMetricsAuditFn('observedLastVisualChange'),
},
{
name: 'First CPU Idle',
id: 'ttfi',
getTs: findValueInMetricsAuditFn('firstCPUIdle', 'timestamp'),
getTiming: findValueInMetricsAuditFn('firstCPUIdle', 'timing'),
getTs: findValueInMetricsAuditFn('firstCPUIdleTs'),
getTiming: findValueInMetricsAuditFn('firstCPUIdle'),
},
{
name: 'Interactive',
id: 'tti',
getTs: findValueInMetricsAuditFn('interactive', 'timestamp'),
getTiming: findValueInMetricsAuditFn('interactive', 'timing'),
getTs: findValueInMetricsAuditFn('interactiveTs'),
getTiming: findValueInMetricsAuditFn('interactive'),
},
{
name: 'End of Trace',
id: 'eot',
getTs: findValueInMetricsAuditFn('observedTraceEnd', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedTraceEnd', 'timing'),
getTs: findValueInMetricsAuditFn('observedTraceEndTs'),
getTiming: findValueInMetricsAuditFn('observedTraceEnd'),
},
{
name: 'On Load',
id: 'onload',
getTs: findValueInMetricsAuditFn('observedLoad', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedLoad', 'timing'),
getTs: findValueInMetricsAuditFn('observedLoadTs'),
getTiming: findValueInMetricsAuditFn('observedLoad'),
},
{
name: 'DOM Content Loaded',
id: 'dcl',
getTs: findValueInMetricsAuditFn('observedDomContentLoaded', 'timestamp'),
getTiming: findValueInMetricsAuditFn('observedDomContentLoaded', 'timing'),
getTs: findValueInMetricsAuditFn('observedDomContentLoadedTs'),
getTiming: findValueInMetricsAuditFn('observedDomContentLoaded'),
},
];
}
Expand Down
25 changes: 16 additions & 9 deletions lighthouse-core/test/audits/metrics-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,39 @@ describe('Performance: metrics', () => {
const result = await Audit.audit(artifacts, {settings});

assert.deepStrictEqual(result.details.items[0], {
metricName: 'firstContentfulPaint',
timing: 2038,
timestamp: undefined,
});

const metrics = {};
result.details.items.forEach(item => metrics[item.metricName] = Math.round(item.timing));

assert.deepStrictEqual(metrics, {
firstContentfulPaint: 2038,
firstContentfulPaintTs: undefined,
firstMeaningfulPaint: 2851,
firstMeaningfulPaintTs: undefined,
firstCPUIdle: 5308,
firstCPUIdleTs: undefined,
interactive: 5308,
interactiveTs: undefined,
speedIndex: 2063,
speedIndexTs: undefined,
estimatedInputLatency: 104,
estimatedInputLatencyTs: undefined,

observedNavigationStart: 0,
observedNavigationStartTs: 225414172015,
observedFirstPaint: 499,
observedFirstPaintTs: 225414670868,
observedFirstContentfulPaint: 499,
observedFirstContentfulPaintTs: 225414670885,
observedFirstMeaningfulPaint: 783,
observedFirstMeaningfulPaintTs: 225414955343,
observedTraceEnd: 12540,
observedTraceEndTs: 225426711887,
observedLoad: 2199,
observedLoadTs: 225416370913,
observedDomContentLoaded: 560,
observedDomContentLoadedTs: 225414732309,
observedFirstVisualChange: 520,
observedFirstVisualChangeTs: 225414692015,
observedLastVisualChange: 818,
observedLastVisualChangeTs: 225414990015,
observedSpeedIndex: 605,
observedSpeedIndexTs: 225414776724,
});
});
});
Loading