-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Report page load asset size #66224
Report page load asset size #66224
Changes from all commits
37365bf
bf24b82
4b1a9e0
ba5f384
8e9f0b1
d4e2f0c
fc06477
6b9e68c
1551910
816cb4b
bebd189
4aa25a6
fb37a19
d9133d3
1d4cb34
cf8dd9c
f379b60
34047f7
ffb7da9
bc56b6b
4554940
c1b092a
060a844
c18ce02
b15ce8c
8a7cc12
da9a923
bd6c908
bf8a0a8
342afd2
f848c4e
7b04014
f440143
5b16040
6b3e7fa
3007539
19979b5
6cfa219
ecdb569
1421388
a6946e0
ddc5617
a19828f
fec3c4e
d6ee29b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import { ToolingLog } from '@kbn/dev-utils'; | ||
import { NavigationOptions, createUrl, navigateToApps } from './navigation'; | ||
|
||
export async function capturePageLoadMetrics(log: ToolingLog, options: NavigationOptions) { | ||
const responsesByPageView = await navigateToApps(log, options); | ||
|
||
const assetSizeMeasurements = new Map<string, number[]>(); | ||
|
||
const numberOfPagesVisited = responsesByPageView.size; | ||
|
||
for (const [, frameResponses] of responsesByPageView) { | ||
for (const [, { url, dataLength }] of frameResponses) { | ||
if (url.length === 0) { | ||
throw new Error('navigateToApps(); failed to identify the url of the request'); | ||
} | ||
if (assetSizeMeasurements.has(url)) { | ||
assetSizeMeasurements.set(url, [dataLength].concat(assetSizeMeasurements.get(url) || [])); | ||
} else { | ||
assetSizeMeasurements.set(url, [dataLength]); | ||
} | ||
} | ||
} | ||
|
||
return Array.from(assetSizeMeasurements.entries()) | ||
.map(([url, measurements]) => { | ||
const baseUrl = createUrl('/', options.appConfig.url); | ||
const relativeUrl = url | ||
// remove the baseUrl (expect the trailing slash) to make url relative | ||
.replace(baseUrl.slice(0, -1), '') | ||
// strip the build number from asset urls | ||
.replace(/^\/\d+\//, '/'); | ||
return [relativeUrl, measurements] as const; | ||
}) | ||
.filter(([url, measurements]) => { | ||
if (measurements.length !== numberOfPagesVisited) { | ||
// ignore urls seen only on some pages | ||
return false; | ||
} | ||
|
||
if (url.startsWith('data:')) { | ||
// ignore data urls since they are already counted by other assets | ||
return false; | ||
} | ||
|
||
if (url.startsWith('/api/') || url.startsWith('/internal/')) { | ||
// ignore api requests since they don't have deterministic sizes | ||
return false; | ||
} | ||
|
||
const allMetricsAreEqual = measurements.every((x, i) => | ||
i === 0 ? true : x === measurements[i - 1] | ||
); | ||
if (!allMetricsAreEqual) { | ||
throw new Error(`measurements for url [${url}] are not equal [${measurements.join(',')}]`); | ||
} | ||
|
||
return true; | ||
}) | ||
.map(([url, measurements]) => { | ||
return { group: 'page load asset size', id: url, value: measurements[0] }; | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import Url from 'url'; | ||
|
||
import { run, createFlagError } from '@kbn/dev-utils'; | ||
import { resolve, basename } from 'path'; | ||
import { capturePageLoadMetrics } from './capture_page_load_metrics'; | ||
|
||
const defaultScreenshotsDir = resolve(__dirname, 'screenshots'); | ||
|
||
export function runPageLoadMetricsCli() { | ||
run( | ||
async ({ flags, log }) => { | ||
const kibanaUrl = flags['kibana-url']; | ||
if (!kibanaUrl || typeof kibanaUrl !== 'string') { | ||
throw createFlagError('Expect --kibana-url to be a string'); | ||
} | ||
|
||
const parsedUrl = Url.parse(kibanaUrl); | ||
|
||
const [username, password] = parsedUrl.auth | ||
? parsedUrl.auth.split(':') | ||
: [flags.username, flags.password]; | ||
|
||
if (typeof username !== 'string' || typeof password !== 'string') { | ||
throw createFlagError( | ||
'Mising username and/or password, either specify in --kibana-url or pass --username and --password' | ||
); | ||
} | ||
|
||
const headless = !flags.head; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why don't use the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default option is |
||
|
||
const screenshotsDir = flags.screenshotsDir || defaultScreenshotsDir; | ||
|
||
if (typeof screenshotsDir !== 'string' || screenshotsDir === basename(screenshotsDir)) { | ||
throw createFlagError('Expect screenshotsDir to be valid path string'); | ||
} | ||
|
||
const metrics = await capturePageLoadMetrics(log, { | ||
headless, | ||
appConfig: { | ||
url: kibanaUrl, | ||
username, | ||
password, | ||
}, | ||
screenshotsDir, | ||
}); | ||
for (const metric of metrics) { | ||
log.info(`${metric.id}: ${metric.value}`); | ||
} | ||
}, | ||
{ | ||
description: `Loads several pages with Puppeteer to capture the size of assets`, | ||
flags: { | ||
string: ['kibana-url', 'username', 'password', 'screenshotsDir'], | ||
boolean: ['head'], | ||
default: { | ||
username: 'elastic', | ||
password: 'changeme', | ||
debug: true, | ||
screenshotsDir: defaultScreenshotsDir, | ||
}, | ||
help: ` | ||
--kibana-url Url for Kibana we should connect to, can include login info | ||
--head Run puppeteer with graphical user interface | ||
--username Set username, defaults to 'elastic' | ||
--password Set password, defaults to 'changeme' | ||
--screenshotsDir Set screenshots directory, defaults to '${defaultScreenshotsDir}' | ||
`, | ||
}, | ||
} | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
export interface ResponseReceivedEvent { | ||
frameId: string; | ||
loaderId: string; | ||
requestId: string; | ||
response: Record<string, any>; | ||
timestamp: number; | ||
type: string; | ||
} | ||
|
||
export interface DataReceivedEvent { | ||
encodedDataLength: number; | ||
dataLength: number; | ||
requestId: string; | ||
timestamp: number; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
export * from './cli'; | ||
export { capturePageLoadMetrics } from './capture_page_load_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.
The case when using
loop
might simplify reading.