Сетевой лог сценариев, bench запросов, pg_stat_statements и EXPLAIN, aggregate исключает трассировочные повторы (O-9). Refs #202 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
2.7 KiB
TypeScript
49 lines
2.7 KiB
TypeScript
/**
|
|
* Сетевой лог сценариев (Task 9): все запросы браузера к API -> perf/results/<run_id>/network.jsonl.
|
|
* Формат строки: perfkit/bench.py (docstring). Подключается во всех сценариях одной строкой после newContext:
|
|
* const nl = attachNetlog(ctx, { scenario, target, role }); ... finally { await nl.flush(); await ctx.close(); }
|
|
* Размер тела - request.sizes().responseBodySize (по CDP, работает и для ответов 30-47 МБ, где response.body() нет).
|
|
* Тело запроса пишется для не-GET (для повтора в bench); ограничено 64 КБ.
|
|
*/
|
|
import type { BrowserContext, Request } from '@playwright/test';
|
|
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { API, WEB } from './auth';
|
|
import { runDir, runId } from './results';
|
|
|
|
export interface NetMeta { scenario: string; target: string; role: string }
|
|
const MAX_POST = 64 * 1024;
|
|
|
|
export function netlogFile(): string { return path.join(runDir(), 'network.jsonl'); }
|
|
|
|
export function attachNetlog(ctx: BrowserContext, meta: NetMeta): { flush: () => Promise<void> } {
|
|
const pending = new Set<Promise<void>>();
|
|
const handle = async (req: Request) => {
|
|
const url = req.url();
|
|
// фронт ходит в API через proxy своего origin (:8080/api), прямые вызовы - на PERF_API
|
|
if (!(url.startsWith(WEB + '/api/') || url.startsWith(API + '/api/'))) return;
|
|
let status = 0, body_size = -1;
|
|
try { status = (await req.response())?.status() ?? 0; } catch { /* контекст закрыт */ }
|
|
try { body_size = (await req.sizes()).responseBodySize; } catch { /* контекст закрыт */ }
|
|
const t = req.timing();
|
|
const post = req.method() === 'GET' ? null : req.postData();
|
|
const u = new URL(url);
|
|
const rec = {
|
|
run_id: runId(), ts: new Date().toISOString(), ...meta,
|
|
method: req.method(), url, path: u.pathname, query: u.search.replace(/^\?/, ''), status,
|
|
resource_type: req.resourceType(),
|
|
post_data: post && post.length <= MAX_POST ? post : null,
|
|
duration_ms: t.responseEnd >= 0 ? Math.round(t.responseEnd * 10) / 10 : null,
|
|
ttfb_ms: t.responseStart >= 0 ? Math.round(t.responseStart * 10) / 10 : null,
|
|
body_size,
|
|
};
|
|
mkdirSync(runDir(), { recursive: true });
|
|
appendFileSync(netlogFile(), JSON.stringify(rec) + '\n', 'utf-8');
|
|
};
|
|
ctx.on('requestfinished', (req) => {
|
|
const p = handle(req).catch((e) => { if (process.env.PERF_NETLOG_DEBUG) console.log("netlog err", e); }).finally(() => pending.delete(p));
|
|
pending.add(p);
|
|
});
|
|
return { flush: async () => { await Promise.all([...pending]); } };
|
|
}
|