UNPKG

browsertime

Version:

Get performance metrics from your web page using Browsertime.

268 lines (254 loc) 7.37 kB
import groupBy from 'lodash.groupby'; import intel from 'intel'; const log = intel.getLogger('browsertime'); export function formatMetric(name, metric, multiple, inMs, extras) { if (metric === undefined) return; function fmt(value) { if (inMs) { return value < 1000 ? value + 'ms' : (value / 1000).toFixed(2) + 's'; } else return value; } let formatted = `${name}: ${fmt(multiple ? metric.median : metric)}`; if (extras) { formatted += ` (σ${fmt(metric.stddev.toFixed(2))} ${ metric.stddev > 0 ? metric.rsd.toFixed(1) : '0' }%)`; } return formatted; } export function logResultLogLine(results) { let index = 0; for (let result of results) { let totalSize = 0; let requests = ''; let firstPaint = '', domContent = '', pageLoad = '', speedIndex = '', perceptualSpeedIndex = '', contentfulSpeedIndex = '', startRender = '', visualComplete85 = '', backEndTime = '', lastVisualChange = '', memory = '', lcp = '', fcp = '', cls = '', tbt = '', cpuBenchmark = '', nRuns = result.browserScripts.length, m = nRuns > 1; if (results.har && results.har.log.pages[index]) { // get the id let pageId = results.har.log.pages[index].id; let entriesByPage = groupBy(results.har.log.entries, 'pageref'); requests = entriesByPage[pageId] ? entriesByPage[pageId].length + ' requests' : ''; if (entriesByPage[pageId]) { for (const request of entriesByPage[pageId]) { // transfer size totalSize += request.response.bodySize; } } totalSize = totalSize > 1024 ? (totalSize / 1024).toFixed(2) + ' kb' : totalSize + ' bytes'; } if (result.statistics.browser && result.statistics.browser.cpuBenchmark) { cpuBenchmark = formatMetric( 'CPUBenchmark', result.statistics.browser.cpuBenchmark, true, true, m ); } if (result.statistics.timings && result.statistics.timings.pageTimings) { let pt = result.statistics.timings.pageTimings, t = result.statistics.timings, vm = result.statistics.visualMetrics, pi = result.statistics.pageinfo, cpu = result.statistics.cpu; firstPaint = formatMetric('firstPaint', t.firstPaint, true, true, m); if (t.largestContentfulPaint) { lcp = formatMetric( 'LCP', t.largestContentfulPaint.renderTime, true, true, m ); } if (pi && pi.cumulativeLayoutShift) { cls = formatMetric('CLS', pi.cumulativeLayoutShift, true, false, m); } if (cpu && cpu.longTasks && cpu.longTasks.totalBlockingTime) { tbt = formatMetric( 'TBT', cpu.longTasks.totalBlockingTime, true, true, m ); } if ( result.statistics.timings.paintTiming && result.statistics.timings.paintTiming['first-contentful-paint'] ) { fcp = formatMetric( 'FCP', result.statistics.timings.paintTiming['first-contentful-paint'], true, true, m ); } domContent = formatMetric( 'DOMContentLoaded', pt.domContentLoadedTime, true, true, m ); speedIndex = formatMetric( 'speedIndex', vm ? vm.SpeedIndex : vm, true, true, m ); perceptualSpeedIndex = formatMetric( 'perceptualSpeedIndex', vm ? vm.PerceptualSpeedIndex : vm, true, true, m ); contentfulSpeedIndex = formatMetric( 'contentfulSpeedIndex', vm ? vm.ContentfulSpeedIndex : vm, true, true, m ); startRender = formatMetric( 'firstVisualChange', vm ? vm.FirstVisualChange : vm, true, true, m ); lastVisualChange = formatMetric( 'lastVisualChange', vm ? vm.LastVisualChange : vm, true, true, m ); visualComplete85 = formatMetric( 'visualComplete85', vm ? vm.VisualComplete85 : vm, true, true, m ); pageLoad = formatMetric('Load', pt.pageLoadTime, true, true, m); backEndTime = formatMetric('TTFB', pt.backEndTime, true, true, m); } if (result.statistics.memory) { let mem = result.statistics.memory; for (let value in mem) { mem[value] = Math.round(mem[value] / 1024 / 1024); } memory = formatMetric('Memory', mem, true, false, m); memory = memory + 'mb'; } let lines = [ `${requests}`, `${totalSize > 0 ? totalSize : ''}`, backEndTime, firstPaint, startRender, fcp, domContent, lcp, cls, tbt, cpuBenchmark, pageLoad, memory, speedIndex, perceptualSpeedIndex, contentfulSpeedIndex, visualComplete85, lastVisualChange ], note = m ? ` (${nRuns} runs)` : ''; lines = lines.filter(Boolean).join(', '); log.info(`${result.info.url} ${lines}${note}`); index++; } } export function toArray(arrayLike) { if (arrayLike === undefined || arrayLike === null) { return []; } if (Array.isArray(arrayLike)) { return arrayLike; } return [arrayLike]; } export function jsonifyVisualProgress(visualProgress) { // Original data looks like // "0=0, 1500=81, 1516=81, 1533=84, 1550=84, 1566=84, 1600=95, 1683=95, 1833=100" if (typeof visualProgress === 'string') { const visualProgressArray = []; for (const value of visualProgress.split(', ')) { const [timestamp, percent] = value.split('='); visualProgressArray.push({ timestamp: Number.parseInt(timestamp, 10), percent: Number.parseInt(percent, 10) }); } return visualProgressArray; } return visualProgress; } export function jsonifyKeyColorFrames(keyColorFrames) { // Original data looks like // "FrameName1=[0-133 255-300], FrameName2=[133-255] FrameName3=[]" if (typeof keyColorFrames === 'string') { const keyColorFramesObject = {}; for (const keyColorPair of keyColorFrames.split(', ')) { const [name, values] = keyColorPair.split('='); keyColorFramesObject[name] = []; const rangePairs = values.replace('[', '').replace(']', ''); if (rangePairs) { for (const rangePair of rangePairs.split(' ')) { const [start, end] = rangePair.split('-'); keyColorFramesObject[name].push({ startTimestamp: Number.parseInt(start, 10), endTimestamp: Number.parseInt(end, 10) }); } } } return keyColorFramesObject; } return keyColorFrames; } export function adjustVisualProgressTimestamps( visualProgress, profilerStartTime, recordingStartTime ) { // calculate offset between unix timestamps which represents the profiler start // time and the time of the first frame after the orange frame after recording start for (const value of visualProgress) { value.timestamp += recordingStartTime - profilerStartTime; } return visualProgress; }