1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-25 16:45:00 +02:00

chore(dev): tweak frontend browser diagnostics

This commit is contained in:
syuilo
2026-07-16 19:58:24 +09:00
parent 875554003e
commit 93bbc89c7a
4 changed files with 24 additions and 151 deletions

View File

@@ -90,53 +90,19 @@ export type TabMemory = {
totalBytes: number;
};
export type BrowserConsoleMetrics = {
log: number;
warn: number;
error: number;
info: number;
};
export type BrowserDiagnostics = {
pageErrorCount: number;
console: BrowserConsoleMetrics;
console: Record<'log' | 'warning' | 'error' | 'info', number>;
};
export function createBrowserDiagnostics(): BrowserDiagnostics {
return {
pageErrorCount: 0,
console: {
log: 0,
warn: 0,
error: 0,
info: 0,
},
};
}
export function recordPageError(diagnostics: BrowserDiagnostics) {
diagnostics.pageErrorCount += 1;
}
function isBrowserConsoleMetricKey(value: string): value is keyof BrowserConsoleMetrics {
return value === 'log' || value === 'warn' || value === 'error' || value === 'info';
}
export function recordConsoleMessageType(diagnostics: BrowserDiagnostics, messageType: string) {
const metricKey = messageType === 'warning' ? 'warn' : messageType;
if (!isBrowserConsoleMetricKey(metricKey)) return;
diagnostics.console[metricKey] += 1;
}
export function summarizeBrowserDiagnostics(samples: BrowserDiagnostics[]): BrowserDiagnostics {
if (samples.length === 0) throw new Error('No browser diagnostic samples');
const median = (select: (sample: BrowserDiagnostics) => number) => util.median(samples.map(select));
return {
pageErrorCount: median(sample => sample.pageErrorCount),
console: {
log: median(sample => sample.console.log),
warn: median(sample => sample.console.warn),
warning: median(sample => sample.console.warning),
error: median(sample => sample.console.error),
info: median(sample => sample.console.info),
},
@@ -204,7 +170,10 @@ type PlaywrightBrowserOptions = {
export class HeadlessChromeController {
public networkRequests: NetworkRequest[] = [];
public webSocketConnections: WebSocketConnection[] = [];
private readonly diagnostics = createBrowserDiagnostics();
private readonly diagnostics = {
pageErrorCount: 0,
console: {} as Record<string, number | undefined>,
};
private readonly browser: Browser;
private readonly context: BrowserContext;
public readonly page: Page;
@@ -225,10 +194,14 @@ export class HeadlessChromeController {
this.page.setDefaultTimeout(options.scenarioTimeoutMs);
this.page.setDefaultNavigationTimeout(options.scenarioTimeoutMs);
this.page.on('pageerror', () => {
recordPageError(this.diagnostics);
this.diagnostics.pageErrorCount++;
});
this.page.on('console', message => {
recordConsoleMessageType(this.diagnostics, message.type());
if (this.diagnostics.console[message.type()] == null) {
this.diagnostics.console[message.type()] = 1;
} else {
this.diagnostics.console[message.type()]++;
}
});
}
@@ -441,7 +414,12 @@ export class HeadlessChromeController {
public collectDiagnostics(): BrowserDiagnostics {
return {
pageErrorCount: this.diagnostics.pageErrorCount,
console: { ...this.diagnostics.console },
console: {
log: this.diagnostics.console.log ?? 0,
warning: this.diagnostics.console.warning ?? 0,
error: this.diagnostics.console.error ?? 0,
info: this.diagnostics.console.info ?? 0,
},
};
}

View File

@@ -172,19 +172,8 @@ function getMetric(report: BrowserMeasurement, key: string) {
return report.performance.cdpMetrics[key];
}
export function renderBrowserDiagnosticsRows(base: BrowserMetricsReport, head: BrowserMetricsReport, all = false) {
return [
metricRow('Page errors', base, head, summary => summary.diagnostics.pageErrorCount, sample => sample.diagnostics.pageErrorCount, util.formatNumber, 1, !all),
metricRow('Console log', base, head, summary => summary.diagnostics.console.log, sample => sample.diagnostics.console.log, util.formatNumber, 1, !all),
metricRow('Console warnings', base, head, summary => summary.diagnostics.console.warn, sample => sample.diagnostics.console.warn, util.formatNumber, 1, !all),
metricRow('Console errors', base, head, summary => summary.diagnostics.console.error, sample => sample.diagnostics.console.error, util.formatNumber, 1, !all),
metricRow('Console info', base, head, summary => summary.diagnostics.console.info, sample => sample.diagnostics.console.info, util.formatNumber, 1, !all),
].filter(row => row != null);
}
function renderSummaryTable(base: BrowserMetricsReport, head: BrowserMetricsReport, all = false) {
const rows = [
...renderBrowserDiagnosticsRows(base, head, all),
//metricRow('Scenario duration', base, head, summary => summary.durationMs, sample => sample.durationMs, formatMs),
metricRow('Requests', base, head, summary => summary.network.requestCount, sample => sample.network.requestCount, util.formatNumber, 1, !all),
//metricRow('Failed requests', base, head, summary => summary.network.failedRequestCount, sample => sample.network.failedRequestCount, util.formatNumber),
@@ -215,7 +204,12 @@ function renderSummaryTable(base: BrowserMetricsReport, head: BrowserMetricsRepo
metricRow('WebSocket connections', base, head, summary => summary.network.webSocketConnectionCount, sample => sample.network.webSocketConnectionCount, util.formatNumber, 1, !all),
metricRow('WebSocket sent', base, head, summary => summary.network.webSocketSentBytes, sample => sample.network.webSocketSentBytes, util.formatBytes, 10000, !all),
metricRow('WebSocket received', base, head, summary => summary.network.webSocketReceivedBytes, sample => sample.network.webSocketReceivedBytes, util.formatBytes, 10000, !all),
metricRow('Tab memory', base, head, summary => summary.performance.tabMemory.totalBytes, sample => sample.performance.tabMemory.totalBytes, util.formatBytes, 10000, !all),
metricRow('Page errors', base, head, summary => summary.diagnostics.pageErrorCount, sample => sample.diagnostics.pageErrorCount, util.formatNumber, 1, !all),
metricRow('Console log', base, head, summary => summary.diagnostics.console.log, sample => sample.diagnostics.console.log, util.formatNumber, 1, !all),
metricRow('Console warnings', base, head, summary => summary.diagnostics.console.warn, sample => sample.diagnostics.console.warn, util.formatNumber, 1, !all),
metricRow('Console errors', base, head, summary => summary.diagnostics.console.error, sample => sample.diagnostics.console.error, util.formatNumber, 1, !all),
metricRow('Console info', base, head, summary => summary.diagnostics.console.info, sample => sample.diagnostics.console.info, util.formatNumber, 1, !all),
metricRow('Page-attributed memory', base, head, summary => summary.performance.tabMemory.totalBytes, sample => sample.performance.tabMemory.totalBytes, util.formatBytes, 10000, !all),
].filter(row => row != null);
return [

View File

@@ -1,98 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import assert from 'node:assert/strict';
import test from 'node:test';
import {
createBrowserDiagnostics,
recordConsoleMessageType,
recordPageError,
summarizeBrowserDiagnostics,
type BrowserDiagnostics,
} from './chrome.mts';
import {
renderBrowserDiagnosticsRows,
type BrowserMeasurement,
type BrowserMeasurementSample,
type BrowserMetricsReport,
} from './frontend-browser-report.mts';
function diagnostics(pageErrorCount: number, log: number, warn: number, error: number, info: number): BrowserDiagnostics {
return {
pageErrorCount,
console: { log, warn, error, info },
};
}
function browserReport(label: 'base' | 'head', values: BrowserDiagnostics[]): BrowserMetricsReport {
const samples = values.map((value, index) => ({
round: index + 1,
diagnostics: value,
} as BrowserMeasurementSample));
return {
label,
timestamp: '2026-07-16T00:00:00.000Z',
url: 'http://127.0.0.1:61812',
scenario: 'test',
sampleCount: samples.length,
aggregation: 'median',
summary: {
diagnostics: summarizeBrowserDiagnostics(values),
} as BrowserMeasurement,
samples,
};
}
test('records page errors and only the requested console message types', () => {
const result = createBrowserDiagnostics();
recordPageError(result);
recordPageError(result);
for (const type of ['log', 'warning', 'error', 'info', 'debug', 'trace']) {
recordConsoleMessageType(result, type);
}
assert.deepEqual(result, diagnostics(2, 1, 1, 1, 1));
});
test('summarizes browser diagnostics with a median for every counter', () => {
const result = summarizeBrowserDiagnostics([
diagnostics(0, 10, 20, 30, 40),
diagnostics(4, 14, 24, 34, 44),
diagnostics(2, 12, 22, 32, 42),
diagnostics(3, 13, 23, 33, 43),
diagnostics(1, 11, 21, 31, 41),
]);
assert.deepEqual(result, diagnostics(2, 12, 22, 32, 42));
});
test('renders each changed browser diagnostics metric as a separate row', () => {
const base = browserReport('base', Array.from({ length: 5 }, () => diagnostics(0, 0, 0, 0, 0)));
const head = browserReport('head', Array.from({ length: 5 }, () => diagnostics(1, 1, 1, 1, 1)));
const rows = renderBrowserDiagnosticsRows(base, head).join('\n');
for (const label of ['Page errors', 'Console log', 'Console warnings', 'Console errors', 'Console info']) {
assert.match(rows, new RegExp(`\\*\\*${label}\\*\\*`));
}
});
test('renders browser diagnostics deltas from paired rounds instead of summary medians', () => {
const base = browserReport('base', [0, 9, 10, 100, 101].map(value => diagnostics(value, 0, 0, 0, 0)));
const head = browserReport('head', [10, 100, 101, 0, 9].map(value => diagnostics(value, 0, 0, 0, 0)));
const rows = renderBrowserDiagnosticsRows(base, head);
assert.equal(rows.length, 1);
assert.match(rows[0], /\*\*Page errors\*\*/);
assert.match(rows[0], /\\text\{\+10\}/);
});
test('omits browser diagnostics rows whose paired median does not change', () => {
const values = Array.from({ length: 5 }, () => diagnostics(2, 3, 4, 5, 6));
const base = browserReport('base', values);
const head = browserReport('head', values);
assert.deepEqual(renderBrowserDiagnosticsRows(base, head), []);
});

View File

@@ -152,4 +152,3 @@ jobs:
node-version-file: '.node-version'
- run: node --test .github/scripts/frontend-js-size.test.mts
- run: node --test .github/scripts/memory-stability-util.test.mts
- run: node --test .github/scripts/frontend-browser-report.test.mts