From 453b041698c6826978ec3b7646392a94274747c5 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 21 Jun 2026 22:43:53 +0000 Subject: [PATCH] fix: improve web-probe analyze visibility --- scripts/src/hwlab-node-impl.ts | 66 +++++++++++++++++-- .../hwlab-node-web-observe-runner-source.ts | 51 +++++++++++++- 2 files changed, 109 insertions(+), 8 deletions(-) diff --git a/scripts/src/hwlab-node-impl.ts b/scripts/src/hwlab-node-impl.ts index 5ff6b31f..7ec6e634 100644 --- a/scripts/src/hwlab-node-impl.ts +++ b/scripts/src/hwlab-node-impl.ts @@ -7486,6 +7486,59 @@ function renderWebObserveAnalyzeTable(value: Record): string { const slowApis = Array.isArray(analysis?.pagePerformanceSlowApi) ? analysis.pagePerformanceSlowApi.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; const findings = Array.isArray(analysis?.findings) ? analysis.findings.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; const domDiagnostics = Array.isArray(analysis?.domDiagnosticGroups) ? analysis.domDiagnosticGroups.map(record).filter((item): item is Record => item !== null).slice(0, 5) : []; + const domDiagnosticSamples = Array.isArray(analysis?.domDiagnosticSamples) ? analysis.domDiagnosticSamples.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; + const consoleAlertGroups = Array.isArray(analysis?.consoleAlertGroups) ? analysis.consoleAlertGroups.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; + const consoleAlertSamples = Array.isArray(analysis?.consoleAlertSamples) ? analysis.consoleAlertSamples.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; + const recentUpdateJumps = Array.isArray(analysis?.turnTimingRecentUpdateJumps) ? analysis.turnTimingRecentUpdateJumps.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; + const domDiagnosticRows = domDiagnostics.length > 0 + ? domDiagnostics.map((item) => [ + webObserveText(item.count), + webObserveShort(webObserveText(item.firstAt), 24), + webObserveShort(webObserveText(item.lastAt), 24), + webObserveShort(webObserveText(item.text), 96), + ]) + : domDiagnosticSamples.map((item) => [ + webObserveText(item.seq), + webObserveShort(webObserveText(item.ts), 24), + webObserveShort([ + webObserveText(item.source), + webObserveText(item.diagnosticCode), + item.traceId ? `trace=${webObserveText(item.traceId)}` : "", + item.httpStatus !== undefined && item.httpStatus !== null ? `http=${webObserveText(item.httpStatus)}` : "", + item.idleSeconds !== undefined && item.idleSeconds !== null ? `idle=${webObserveText(item.idleSeconds)}` : "", + item.waitingFor ? `waitingFor=${webObserveText(item.waitingFor)}` : "", + item.lastEventLabel ? `lastEvent=${webObserveText(item.lastEventLabel)}` : "", + ].filter((part) => part !== "" && part !== "-").join(" "), 64), + webObserveShort(webObserveText(item.text), 96), + ]); + const consoleAlertRows = consoleAlertGroups.length > 0 + ? consoleAlertGroups.map((item) => [ + webObserveText(item.count), + webObserveShort(webObserveText(item.type), 14), + webObserveText(item.status), + webObserveShort(webObserveText(item.path), 52), + webObserveShort(webObserveText(item.lastAt ?? item.firstAt), 24), + webObserveShort(webObserveArray(item.traceIds).join(",") || "-", 28), + ]) + : consoleAlertSamples.map((item) => [ + webObserveShort(webObserveText(item.ts), 24), + webObserveShort(webObserveText(item.type), 14), + webObserveText(item.status), + webObserveShort(webObserveText(item.path), 52), + webObserveShort(webObserveText(item.traceId), 28), + webObserveShort(webObserveText(item.text), 72), + ]); + const recentUpdateJumpRows = recentUpdateJumps.map((item) => [ + webObserveShort(webObserveText(item.columnLabel), 14), + webObserveText(item.promptIndex), + webObserveText(item.fromSeq), + webObserveText(item.toSeq), + `${webObserveText(item.fromValue)}->${webObserveText(item.toValue)}`, + webObserveText(item.delta), + webObserveText(item.sampleDeltaSeconds), + webObserveText(item.allowedIncreaseSeconds), + webObserveShort(webObserveText(item.traceId), 24), + ]); const lines = [ `hwlab nodes web-probe observe analyze (${webObserveText(value.status)})`, "", @@ -7549,6 +7602,9 @@ function renderWebObserveAnalyzeTable(value: Record): string { webObserveShort(webObserveText(item.source), 24), ]) : [["-", "-", "-", "-", "-", "-"]]), "", + "Recent update jumps:", + webObserveTable(["TURN", "PROMPT", "FROM_SEQ", "TO_SEQ", "VALUE", "DELTA", "SAMPLE_S", "ALLOWED", "TRACE"], recentUpdateJumpRows.length > 0 ? recentUpdateJumpRows : [["-", "-", "-", "-", "-", "-", "-", "-", "-"]]), + "", "Runtime alerts:", webObserveTable(["HTTP_ERR", "REQUEST_FAILED", "DOM_DIAG", "CONSOLE_ALERT", "PROMPT_SEGMENTS", "PROMPT_NETWORK"], [[ webObserveText(runtimeAlerts?.httpErrorCount), @@ -7569,12 +7625,10 @@ function renderWebObserveAnalyzeTable(value: Record): string { ]) : [["-", "-", "-", "-", "-"]]), "", "DOM diagnostics:", - webObserveTable(["COUNT", "FIRST", "LAST", "TEXT"], domDiagnostics.length > 0 ? domDiagnostics.map((item) => [ - webObserveText(item.count), - webObserveShort(webObserveText(item.firstAt), 24), - webObserveShort(webObserveText(item.lastAt), 24), - webObserveShort(webObserveText(item.text), 96), - ]) : [["-", "-", "-", "-"]]), + webObserveTable(domDiagnostics.length > 0 ? ["COUNT", "FIRST", "LAST", "TEXT"] : ["SEQ", "TS", "META", "TEXT"], domDiagnosticRows.length > 0 ? domDiagnosticRows : [["-", "-", "-", "-"]]), + "", + "Console alerts:", + webObserveTable(consoleAlertGroups.length > 0 ? ["COUNT", "TYPE", "STATUS", "PATH", "LAST", "TRACES"] : ["TS", "TYPE", "STATUS", "PATH", "TRACE", "TEXT"], consoleAlertRows.length > 0 ? consoleAlertRows : [["-", "-", "-", "-", "-", "-"]]), "", "Findings:", webObserveTable(["KIND", "SEVERITY", "COUNT", "SUMMARY"], findings.length > 0 ? findings.map((item) => [ diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 983c3df2..669d73e1 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -1167,6 +1167,53 @@ console.log(JSON.stringify({ traceIds: Array.isArray(item.traceIds) ? item.traceIds.slice(0, 4) : [], text: String(item.preview ?? item.normalizedPreview ?? item.text ?? "").slice(0, 160), })), + domDiagnosticSamples: runtimeAlerts.domDiagnostics.slice(-8).map((item) => ({ + seq: item.seq ?? null, + ts: item.ts ?? null, + promptIndex: item.promptIndex ?? null, + source: item.source ?? null, + diagnosticCode: item.diagnosticCode ?? null, + traceId: item.traceId ?? null, + httpStatus: item.httpStatus ?? null, + idleSeconds: item.idleSeconds ?? null, + waitingFor: item.waitingFor ?? null, + lastEventLabel: item.lastEventLabel ?? null, + text: String(item.preview ?? item.text ?? "").slice(0, 180), + })), + consoleAlertGroups: runtimeAlerts.consoleAlertsByPath.slice(0, 8).map((item) => ({ + type: item.type ?? null, + status: item.status ?? null, + path: item.urlPath ?? null, + count: item.count, + firstAt: item.firstAt ?? null, + lastAt: item.lastAt ?? null, + promptIndexes: Array.isArray(item.promptIndexes) ? item.promptIndexes.slice(0, 6) : [], + traceIds: Array.isArray(item.traceIds) ? item.traceIds.slice(0, 4) : [], + })), + consoleAlertSamples: runtimeAlerts.consoleAlerts.slice(-8).map((item) => ({ + ts: item.ts ?? null, + promptIndex: item.promptIndex ?? null, + type: item.type ?? null, + status: item.status ?? null, + path: item.urlPath ?? null, + traceId: item.traceId ?? null, + text: String(item.preview ?? item.text ?? "").slice(0, 180), + })), + turnTimingRecentUpdateJumps: sampleMetrics.turnTimingRecentUpdateSawtoothJumps.slice(0, 8).map((item) => ({ + columnLabel: item.columnLabel ?? item.columnId ?? null, + promptIndex: item.promptIndex ?? null, + traceId: item.traceId ?? null, + fromSeq: item.fromSeq ?? null, + toSeq: item.toSeq ?? null, + fromTs: item.fromTs ?? null, + toTs: item.toTs ?? null, + fromValue: item.fromValue ?? null, + toValue: item.toValue ?? null, + delta: item.delta ?? null, + sampleDeltaSeconds: item.sampleDeltaSeconds ?? null, + allowedIncreaseSeconds: item.allowedIncreaseSeconds ?? null, + excessiveIncreaseSeconds: item.excessiveIncreaseSeconds ?? null, + })), pagePerformanceSlowApi: pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount })), findings: findings.slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })), valuesRedacted: true, @@ -2097,7 +2144,7 @@ function groupNetworkAlerts(events) { function isDiagnosticText(text) { const value = String(text || ""); - return /Failed to (?:fetch|load resource)|request failed|net::ERR_[A-Z0-9_:-]+|server responded with a status of [45][0-9]{2}|HTTP\s+[45][0-9]{2}|trace_id=|workbench turn\s*超过|无新活动|idle\s+\d+s|waitingFor=|lastEventLabel=|无法连接上游|(?:报警|告警|警告|错误|异常|超时)[::]|timeout[::=]|error[::=]|failed[::=]|warning[::=]|provider-unavailable|agentrun:error|AgentRun error|projection-resume|durable projection store|realtime-gap|\b50[234]\b/iu.test(value); + return /Failed to (?:fetch|load resource)|request failed|net::ERR_[A-Z0-9_:-]+|server responded with a status of [45][0-9]{2}|HTTP\s+[45][0-9]{2}\b|trace_id=|workbench turn\s*超过|turn\s*超过|无新活动|idle\s+\d+s|waitingFor=|lastEventLabel=|无法连接上游|代理暂时无法连接上游|provider-unavailable|agentrun:error|AgentRun error|projection-resume|sync-failed|durable projection store|realtime-gap|Trace 更新超时|加载失败|请求失败|请求已失败/iu.test(value); } function isTerminalTraceText(text) { @@ -2121,7 +2168,7 @@ function buildSampleMetrics(samples, control) { const promptIndex = Number.isFinite(tsMs) ? latestPromptIndex(promptTimes, tsMs) : 0; const totalElapsedValues = texts.flatMap(parseTotalElapsedSeconds).filter(Number.isFinite); const recentUpdateValues = texts.flatMap(parseRecentUpdateSeconds).filter(Number.isFinite); - const diagnosticTexts = texts.filter((text) => /Failed to fetch|realtime-gap|durable projection store|turn 超过|无法连接|projection-resume|503|timeout|error/iu.test(text)).slice(0, 5); + const diagnosticTexts = texts.filter(isDiagnosticText).slice(0, 5); const terminalTexts = texts.filter(isTerminalTraceText).slice(0, 5); const finalResultTexts = texts.filter(isFinalResultText).slice(0, 5); return {