/** * Сетевой лог сценариев (Task 9): все запросы браузера к API -> perf/results//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 } { const pending = new Set>(); 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]); } }; }