Skip to content

Commit

Permalink
core(metrics): consumable metrics audit output (GoogleChrome#5101)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce authored and kdzwinel committed Aug 16, 2018
1 parent 59aa6fe commit a78ed60
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 174 deletions.
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];
};
}

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

0 comments on commit a78ed60

Please sign in to comment.