Statistics for PageXray per URL (#4061)

* Collect the standard PageXray statistics
This commit is contained in:
Peter Hedenskog 2024-01-15 22:34:32 +01:00 committed by GitHub
parent 28916076fc
commit f3a28fa731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 156 additions and 37 deletions

View File

@ -105,7 +105,6 @@ export default class PageXrayPlugin extends SitespeedioPlugin {
}
}
this.pageXrayAggregator.addToAggregate(pageSummary, group);
if (this.multi) {
// The HAR file can have multiple URLs
const sentURL = {};
@ -116,6 +115,9 @@ export default class PageXrayPlugin extends SitespeedioPlugin {
sentURL[summary.url] += 1;
} else {
sentURL[summary.url] = 1;
summary.statistics = this.pageXrayAggregator.summarizePerUrl(
summary.url
);
queue.postMessage(
make('pagexray.pageSummary', summary, {
url: summary.url,
@ -134,6 +136,9 @@ export default class PageXrayPlugin extends SitespeedioPlugin {
);
}
} else {
pageSummary[0].statistics = this.pageXrayAggregator.summarizePerUrl(
message.url
);
queue.postMessage(
make('pagexray.pageSummary', pageSummary[0], {
url: message.url,

View File

@ -1,11 +1,16 @@
import forEach from 'lodash.foreach';
import { pushGroupStats, setStatsSummary } from '../../support/statsHelpers.js';
import {
pushGroupStats,
setStatsSummary,
pushStats
} from '../../support/statsHelpers.js';
const METRIC_NAMES = ['transferSize', 'contentSize', 'requests'];
export class PageXrayAggregator {
constructor() {
this.urls = {};
this.stats = {};
this.groups = {};
}
@ -19,6 +24,11 @@ export class PageXrayAggregator {
let groups = this.groups;
for (const summary of pageSummary) {
let url = summary.url;
if (this.urls[url] === undefined) {
this.urls[url] = {};
}
// stats for the whole page
for (const metric of METRIC_NAMES) {
// There's a bug in Firefox/https://github.com/devtools-html/har-export-trigger
@ -26,6 +36,7 @@ export class PageXrayAggregator {
// https://github.com/sitespeedio/sitespeed.io/issues/2090
if (!Number.isNaN(summary[metric])) {
pushGroupStats(stats, groups[group], metric, summary[metric]);
pushStats(this.urls[url], metric, summary[metric]);
}
}
@ -41,6 +52,12 @@ export class PageXrayAggregator {
'contentTypes.' + contentType + '.' + metric,
summary.contentTypes[contentType][metric]
);
pushStats(
this.urls[url],
'contentTypes.' + contentType + '.' + metric,
summary.contentTypes[contentType][metric]
);
}
}
}
@ -52,6 +69,12 @@ export class PageXrayAggregator {
'responseCodes.' + responseCode,
summary.responseCodes[responseCode]
);
pushStats(
this.urls[url],
'responseCodes.' + responseCode,
summary.responseCodes[responseCode]
);
}
/*
for (const responseCode of Object.keys(summary.responseCodes)) {
@ -73,6 +96,11 @@ export class PageXrayAggregator {
'firstParty' + '.' + metric,
summary.firstParty[metric]
);
pushStats(
this.urls[url],
'firstParty' + '.' + metric,
summary.firstParty[metric]
);
}
if (summary.thirdParty[metric] !== undefined) {
pushGroupStats(
@ -81,6 +109,12 @@ export class PageXrayAggregator {
'thirdParty' + '.' + metric,
summary.thirdParty[metric]
);
pushStats(
this.urls[url],
'thirdParty' + '.' + metric,
summary.thirdParty[metric]
);
}
}
}
@ -107,6 +141,9 @@ export class PageXrayAggregator {
});
}
}
summarizePerUrl(url) {
return this.summarizePerObject(this.urls[url]);
}
summarize() {
if (Object.keys(this.stats).length === 0) {
return;

View File

@ -239,177 +239,249 @@ export default {
},
pagexray: {
requests: {
total: { path: 'requests', name: 'Total Requests', format: noop },
total: {
path: 'statistics.requests.median',
summaryPath: 'requests',
runPath: 'requests',
name: 'Total Requests',
format: noop
},
html: {
path: 'contentTypes.html.requests',
path: 'statistics.contentTypes.html.requests.median',
summaryPath: 'contentTypes.html.requests',
runPath: 'contentTypes.html.requests',
name: 'HTML Requests',
format: noop
},
javascript: {
path: 'rontentTypes.javascript.requests',
path: 'statistics.contentTypes.javascript.requests.median',
summaryPath: 'contentTypes.javascript.requests',
runPath: 'contentTypes.javascript.requests',
name: 'JavaScript Requests',
format: noop
},
css: {
path: 'contentTypes.css.request',
path: 'statistics.contentTypes.css.requests.median',
summaryPath: 'contentTypes.css.requests',
runPath: 'contentTypes.css.requests',
name: 'CSS Requests',
format: noop
},
image: {
path: 'contentTypes.image.requests',
path: 'statistics.contentTypes.images.requests.median',
summaryPath: 'contentTypes.images.requests',
runPath: 'contentTypes.images.requests',
name: 'Image Requests',
format: noop
},
font: {
path: 'contentTypes.font.requests',
path: 'statistics.contentTypes.font.requests.median',
summaryPath: 'contentTypes.font.requests',
runPath: 'contentTypes.font.requests',
name: 'Font Requests',
format: noop
},
httpErrors: {
path: 'responseCodes',
path: 'statistics.responseCodes.median',
summaryPath: 'responseCodes',
runPath: 'responseCodes',
name: 'HTTP Errors',
format: httpErrors
}
},
transferSize: {
total: {
path: 'transferSize',
path: 'statistics.transferSize.median',
summaryPath: 'transferSize',
runPath: 'transferSize',
name: 'Total Transfer Size',
format: size.format
},
html: {
path: 'contentTypes.html.transferSize',
path: 'statistics.contentTypes.html.transferSize.median',
summaryPath: 'contentTypes.html.transferSize',
runPath: 'contentTypes.html.transferSize',
name: 'HTML Transfer Size',
format: size.format
},
javascript: {
path: 'contentTypes.javascript.transferSize',
path: 'statistics.contentTypes.javascript.transferSize.median',
summaryPath: 'contentTypes.javascript.transferSize',
runPath: 'contentTypes.javascript.transferSize',
name: 'JavaScript Transfer Size',
format: size.format
},
css: {
path: 'contentTypes.css.transferSize',
path: 'statistics.contentTypes.css.transferSize.median',
summaryPath: 'contentTypes.css.transferSize',
runPath: 'contentTypes.css.transferSize',
name: 'CSS Transfer Size',
format: size.format
},
image: {
path: 'contentTypes.image.transferSize',
path: 'statistics.contentTypes.image.transferSize.median',
summaryPath: 'contentTypes.image.transferSize',
runPath: 'contentTypes.iamge.transferSize',
name: 'Image Transfer Size',
format: size.format
},
font: {
path: 'contentTypes.font.transferSize',
path: 'statistics.contentTypes.font.transferSize.median',
summaryPath: 'contentTypes.font.transferSize',
runPath: 'contentTypes.font.transferSize',
name: 'Font Transfer Size',
format: size.format
},
favicon: {
path: 'contentTypes.favicon.transferSize',
path: 'statistics.contentTypes.favicon.transferSize.median',
summaryPath: 'contentTypes.favicon.transferSize',
runPath: 'contentTypes.favicon.transferSize',
name: 'Favicon Transfer Size',
format: size.format
},
json: {
path: 'contentTypes.json.transferSize',
path: 'statistics.contentTypes.json.transferSize.median',
summaryPath: 'contentTypes.json.transferSize',
runPath: 'contentTypes.json.transferSize',
name: 'JSON Transfer Size',
format: size.format
},
other: {
path: 'contentTypes.other.transferSize',
path: 'statistics.contentTypes.other.transferSize.median',
summaryPath: 'contentTypes.other.transferSize',
runPath: 'contentTypes.other.transferSize',
name: 'Other Transfer Size',
format: size.format
},
plain: {
path: 'contentTypes.plain.transferSize',
path: 'statistics.contentTypes.plain.transferSize.median',
summaryPath: 'contentTypes.plain.transferSize',
runPath: 'contentTypes.plain.transferSize',
name: 'Plain Transfer Size',
format: size.format
},
svg: {
path: 'contentTypes.svg.transferSize',
path: 'statistics.contentTypes.svg.transferSize.median',
summaryPath: 'contentTypes.svg.transferSize',
runPath: 'contentTypes.svg.transferSize',
name: 'SVG Transfer Size',
format: size.format
}
},
contentSize: {
total: {
path: 'contentSize',
path: 'statistics.contentSize.median',
summaryPath: 'contentSize',
runPath: 'contentSize',
name: 'Total Content Size',
format: size.format
},
html: {
path: 'contentTypes.html.contentSize',
path: 'statistics.contentTypes.html.contentSize.median',
summaryPath: 'contentTypes.html.contentSize',
runPath: 'contentTypes.html.contentSize',
name: 'HTML Content Size',
format: size.format
},
javascript: {
path: 'contentTypes.javascript.contentSize',
path: 'statistics.contentTypes.javascript.contentSize.median',
summaryPath: 'contentTypes.javascript.contentSize',
runPath: 'contentTypes.javascript.contentSize',
name: 'JavaScript Content Size',
format: size.format
},
css: {
path: 'contentTypes.css.contentSize',
path: 'statistics.contentTypes.css.contentSize.median',
summaryPath: 'contentTypes.css.contentSize',
runPath: 'contentTypes.css.contentSize',
name: 'CSS Content Size',
format: size.format
},
image: {
path: 'contentTypes.image.contentSize',
path: 'statistics.contentTypes.image.contentSize.median',
summaryPath: 'contentTypes.image.contentSize',
runPath: 'contentTypes.image.contentSize',
name: 'Image Content Size',
format: size.format
},
font: {
path: 'contentTypes.font.contentSize',
path: 'statistics.contentTypes.font.contentSize.median',
summaryPath: 'contentTypes.font.contentSize',
runPath: 'contentTypes.font.contentSize',
name: 'Font Content Size',
format: size.format
},
favicon: {
path: 'contentTypes.favicon.contentSize',
path: 'statistics.contentTypes.favicon.contentSize.median',
summaryPath: 'contentTypes.favicon.contentSize',
runPath: 'contentTypes.favicon.contentSize',
name: 'Favicon Content Size',
format: size.format
},
json: {
path: 'contentTypes.json.contentSize',
path: 'statistics.contentTypes.json.contentSize.median',
summaryPath: 'contentTypes.json.contentSize',
runPath: 'contentTypes.json.contentSize',
name: 'JSON Content Size',
format: size.format
},
other: {
path: 'contentTypes.other.contentSize',
path: 'statistics.contentTypes.other.contentSize.median',
summaryPath: 'contentTypes.other.contentSize',
runPath: 'contentTypes.other.contentSize',
name: 'Other Content Size',
format: size.format
},
plain: {
path: 'contentTypes.plain.contentSize',
path: 'statistics.contentTypes.plain.contentSize.median',
summaryPath: 'contentTypes.plain.contentSize',
runPath: 'contentTypes.plain.contentSize',
name: 'Plain Content Size',
format: size.format
},
svg: {
path: 'contentTypes.svg.contentSize',
path: 'statistics.contentTypes.svg.contentSize.median',
summaryPath: 'contentTypes.svg.contentSize',
runPath: 'contentTypes.svg.contentSize',
name: 'SVG Content Size',
format: size.format
}
},
thirdParty: {
transferSize: {
path: 'thirdParty.transferSize',
path: 'statistics.thirdParty.transferSize.median',
summaryPath: 'thirdParty.transferSize',
runPath: 'thirdParty.transferSize',
name: 'Third Party Transfer Size',
format: size.format
},
requests: {
path: 'thirdParty.requests',
path: 'statistics.thirdParty.requests.median',
summaryPath: 'thirdParty.requests',
runPath: 'thirdParty.requests',
name: 'Third Party Requests',
format: noop
}
},
firstParty: {
requests: {
path: 'firstParty.requests',
path: 'statistics.firstParty.requests.median',
summaryPath: 'firstParty.requests',
runPath: 'firstParty.requests',
name: 'First Party Requests',
format: noop
},
transferSize: {
path: 'firstParty.transferSize',
path: 'statistics.firstParty.transferSize.median',
summaryPath: 'firstParty.transferSize',
runPath: 'firstParty.transferSize',
name: 'First Party Total Transfer Size',
format: size.format
},
contentSize: {
path: 'firstParty.contentSize',
path: 'statistics.firstParty.contentSize.median',
summaryPath: 'firstParty.contentSize',
runPath: 'firstParty.contentSize',
name: 'First Party Total Content Size',
format: size.format
}

View File

@ -48,7 +48,12 @@ export function summarizeStats(stats, options) {
let data = {
median: Number.parseFloat(stats.median().toFixed(decimals)),
mean: Number.parseFloat(stats.amean().toFixed(decimals))
mean: Number.parseFloat(stats.amean().toFixed(decimals)),
rsd:
stats.stddev() > 0
? Number.parseFloat((100 * stats.stddev()) / stats.amean())
: 0, // Relative standard deviation
stddev: Number.parseFloat(stats.stddev().toFixed(decimals))
};
for (const p of percentiles) {
let name = percentileName(p);