Merge pull request #1232 from pikasTech/fix/1213-sentinel-report-timing
补齐哨兵报告 timing 详情可见性
This commit is contained in:
@@ -1110,6 +1110,8 @@ function findingSearchText(item) {
|
||||
item?.rootCause,
|
||||
item?.evidenceSummary,
|
||||
item?.nextAction,
|
||||
item?.timingStatus,
|
||||
item?.timingSourceOfTruth,
|
||||
item?.scenarioId,
|
||||
item?.latestRunId,
|
||||
].filter((value) => value !== null && value !== undefined).join(" ").toLowerCase();
|
||||
@@ -1146,6 +1148,7 @@ function checkDetailRows(item) {
|
||||
{ key: "time", label: "时间", value: checkTimeText(item) },
|
||||
{ key: "scenario", label: "场景", value: safeDetailValue(item?.scenarioId || item?.scenario_id) },
|
||||
{ key: "observer", label: "观察任务", value: safeDetailValue(item?.observerId || item?.observer_id) },
|
||||
{ key: "timing", label: "计时来源", value: checkTimingText(item) },
|
||||
{ key: "report", label: "报告", value: shortHash(item?.reportJsonSha256 || item?.report_json_sha256 || item?.reportSha256 || "") || "-" },
|
||||
].filter((row) => row.value !== "");
|
||||
}
|
||||
@@ -1159,11 +1162,23 @@ function checkEvidenceRows(item) {
|
||||
{ key: "page", label: "页面", value: safeDetailValue(item?.pageRole || evidence.pageRole) },
|
||||
{ key: "command", label: "命令编号", value: safeDetailValue(item?.commandId || evidence.commandId) },
|
||||
{ key: "range", label: "采集范围", value: safeDetailValue(item?.sentinelRange || evidence.sentinelRange) },
|
||||
{ key: "timing", label: "计时状态", value: checkTimingText(item) },
|
||||
{ key: "blocking", label: "阻塞状态", value: item?.blocking === true ? "阻塞" : "非阻塞" },
|
||||
].filter((row) => row.value !== "" && row.value !== "-");
|
||||
return rows.length > 0 ? rows : [{ key: "none", label: "证据摘要", value: "已记录到报告详情。" }];
|
||||
}
|
||||
|
||||
function checkTimingText(item) {
|
||||
const status = item?.timingStatus || "";
|
||||
const source = item?.timingSourceOfTruth || item?.expectedElapsedSource || item?.evidenceKind || "";
|
||||
if (!status && !source) return "";
|
||||
return [
|
||||
status ? `status=${safeDetailValue(status)}` : "",
|
||||
source ? `source=${safeDetailValue(source)}` : "",
|
||||
item?.timingAlert === true ? "alert=true" : "",
|
||||
].filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function safeDetailValue(value) {
|
||||
if (value === null || value === undefined || value === "") return "-";
|
||||
const text = String(value).replace(/\s+/g, " ").trim();
|
||||
|
||||
@@ -3453,6 +3453,9 @@ function compactQuickVerifyRecordFinding(value: unknown): Record<string, unknown
|
||||
rootCauseConfidence: boundQuickVerifyRecordText(item.rootCauseConfidence, 40),
|
||||
nextAction: boundQuickVerifyRecordText(item.nextAction, 240),
|
||||
evidenceSummary: stringAtNullable(item, "evidenceSummary") ?? compactQuickVerifyFindingEvidence(item.evidence),
|
||||
timingSourceOfTruth: boundQuickVerifyRecordText(item.timingSourceOfTruth ?? item.expectedElapsedSource ?? item.evidenceKind, 100),
|
||||
timingStatus: boundQuickVerifyRecordText(item.timingStatus, 60),
|
||||
timingAlert: item.timingAlert === true,
|
||||
blocking: item.blocking === true,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
@@ -4197,7 +4200,7 @@ function readAnalysisSummaryFromWorkspace(state: SentinelCicdState, stateDir: st
|
||||
"let artifactCount=0; let screenshot=null;",
|
||||
"function walk(dir){let entries=[]; try{entries=fs.readdirSync(dir,{withFileTypes:true})}catch{return}; for(const e of entries){const p=path.join(dir,e.name); if(e.isDirectory()) walk(p); else { artifactCount++; if(/\\.png$/i.test(e.name)){const b=read(p); screenshot={path:p,sha256:sha(b),bytes:b?b.length:0}; } } }}",
|
||||
"walk(stateDir);",
|
||||
"const findings=arr(report?.findings ?? report?.archiveSummary?.redFindings).slice(0,20).map((item)=>{const v=rec(item); return {id:clip(v.id??v.kind??v.code,80),kind:clip(v.kind??v.id??v.code,80),code:clip(v.code??v.kind??v.id,80),severity:clip(v.severity??v.level,32),level:clip(v.level??v.severity,32),count:Number(v.count??v.sampleCount??1),summary:clip(v.summary??v.message,220),message:clip(v.message??v.summary,220),rootCause:clip(v.rootCause,140),rootCauseStatus:clip(v.rootCauseStatus,90),rootCauseConfidence:clip(v.rootCauseConfidence,40),nextAction:clip(v.nextAction,240),evidenceSummary:v.evidence?clip(JSON.stringify(rec(v.evidence)),220):clip(v.evidenceSummary,220),blocking:v.blocking===true,afterRound:v.afterRound??null,canarySessionId:clip(v.canarySessionId,80),routeSessionId:clip(v.routeSessionId,80),activeSessionId:clip(v.activeSessionId,80),consecutiveUserMessageCount:v.consecutiveUserMessageCount??null,sentinelRange:clip(v.sentinelRange,80),sampleSeq:v.sampleSeq??null,traceIds:arr(v.traceIds).slice(0,8).map((x)=>clip(x,80)),pageRole:clip(v.pageRole,32),pageId:clip(v.pageId,80),observerId:clip(v.observerId,80),stateDir:clip(v.stateDir,160),commandId:clip(v.commandId,80),valuesRedacted:true};});",
|
||||
"const findings=arr(report?.findings ?? report?.archiveSummary?.redFindings).slice(0,20).map((item)=>{const v=rec(item); return {id:clip(v.id??v.kind??v.code,80),kind:clip(v.kind??v.id??v.code,80),code:clip(v.code??v.kind??v.id,80),severity:clip(v.severity??v.level,32),level:clip(v.level??v.severity,32),count:Number(v.count??v.sampleCount??1),summary:clip(v.summary??v.message,220),message:clip(v.message??v.summary,220),rootCause:clip(v.rootCause,140),rootCauseStatus:clip(v.rootCauseStatus,90),rootCauseConfidence:clip(v.rootCauseConfidence,40),nextAction:clip(v.nextAction,240),evidenceSummary:v.evidence?clip(JSON.stringify(rec(v.evidence)),220):clip(v.evidenceSummary,220),timingSourceOfTruth:clip(v.timingSourceOfTruth??v.expectedElapsedSource??v.evidenceKind,100),timingStatus:clip(v.timingStatus,60),timingAlert:v.timingAlert===true,blocking:v.blocking===true,afterRound:v.afterRound??null,canarySessionId:clip(v.canarySessionId,80),routeSessionId:clip(v.routeSessionId,80),activeSessionId:clip(v.activeSessionId,80),consecutiveUserMessageCount:v.consecutiveUserMessageCount??null,sentinelRange:clip(v.sentinelRange,80),sampleSeq:v.sampleSeq??null,traceIds:arr(v.traceIds).slice(0,8).map((x)=>clip(x,80)),pageRole:clip(v.pageRole,32),pageId:clip(v.pageId,80),observerId:clip(v.observerId,80),stateDir:clip(v.stateDir,160),commandId:clip(v.commandId,80),valuesRedacted:true};});",
|
||||
"const slow=arr(report?.pagePerformanceSlowApi ?? report?.archivePagePerformanceSlowApi).slice(0,8).map((item)=>{const v=rec(item); return {path:clip(v.path??v.route,120),sampleCount:v.sampleCount??null,p95Ms:v.p95Ms??null,maxMs:v.maxMs??null,overFiveSecondCount:v.overFiveSecondCount??null};});",
|
||||
"console.log(JSON.stringify({ok:!!report,reportOk:!!report&&report.ok!==false,stateDir,reportJsonPath:reportPath,reportJsonSha256:sha(jsonBuf),reportMdPath,reportMdSha256:sha(read(reportMdPath)),findingCount:Number(report?.findingCount??findings.length),artifactCount,screenshot,findings,counts:rec(report?.counts),analysisWindow:rec(report?.analysisWindow??report?.windows?.recent?.summary),pagePerformanceSlowApi:slow,valuesRedacted:true}));",
|
||||
"NODE",
|
||||
@@ -4964,7 +4967,7 @@ function renderQuickVerifySummary(input: Record<string, unknown>): string {
|
||||
`publicOrigin=${input.publicOrigin ?? "-"}`,
|
||||
"",
|
||||
"Findings",
|
||||
findings.length === 0 ? "-" : findings.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
||||
findings.length === 0 ? "-" : findings.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"}${formatQuickVerifyTimingSuffix(item)} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -5000,10 +5003,17 @@ function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown
|
||||
commandSteps.length === 0 ? "-" : commandSteps.join("\n"),
|
||||
"",
|
||||
"Findings",
|
||||
findingRows.length === 0 ? "-" : findingRows.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
||||
findingRows.length === 0 ? "-" : findingRows.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"}${formatQuickVerifyTimingSuffix(item)} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function formatQuickVerifyTimingSuffix(item: Record<string, unknown>): string {
|
||||
const status = stringAtNullable(item, "timingStatus");
|
||||
const source = stringAtNullable(item, "timingSourceOfTruth") ?? stringAtNullable(item, "expectedElapsedSource") ?? stringAtNullable(item, "evidenceKind");
|
||||
if (status === null && source === null) return "";
|
||||
return ` timing=${[status === null ? null : `status=${status}`, source === null ? null : `source=${source}`].filter((part) => part !== null).join(" ")}`;
|
||||
}
|
||||
|
||||
function renderMaintenanceResult(result: Record<string, unknown>): string {
|
||||
const serviceHealth = record(result.serviceHealth);
|
||||
const maintenance = record(result.maintenance);
|
||||
|
||||
@@ -870,6 +870,9 @@ function dashboardFindings(config: WebProbeSentinelServiceConfig, db: Database,
|
||||
rootCauseConfidence: stringOrNull(latestDetail?.rootCauseConfidence),
|
||||
nextAction: stringOrNull(latestDetail?.nextAction),
|
||||
evidenceSummary: stringOrNull(latestDetail?.evidenceSummary),
|
||||
timingSourceOfTruth: stringOrNull(latestDetail?.timingSourceOfTruth),
|
||||
timingStatus: stringOrNull(latestDetail?.timingStatus),
|
||||
timingAlert: latestDetail?.timingAlert === true,
|
||||
traceability: latestRun === null ? null : runTraceability(config, latestRun),
|
||||
valuesRedacted: true,
|
||||
});
|
||||
@@ -1147,6 +1150,9 @@ function enrichFindingRowWithStoredDetail(config: WebProbeSentinelServiceConfig,
|
||||
rootCauseConfidence: stringOrNull(detail?.rootCauseConfidence),
|
||||
nextAction: stringOrNull(detail?.nextAction),
|
||||
evidenceSummary: stringOrNull(detail?.evidenceSummary),
|
||||
timingSourceOfTruth: stringOrNull(detail?.timingSourceOfTruth),
|
||||
timingStatus: stringOrNull(detail?.timingStatus),
|
||||
timingAlert: detail?.timingAlert === true,
|
||||
valuesRedacted: true,
|
||||
});
|
||||
}
|
||||
@@ -1184,6 +1190,9 @@ function compactStoredFinding(value: unknown): Record<string, unknown> {
|
||||
rootCauseConfidence: stringOrNull(item.rootCauseConfidence),
|
||||
nextAction: stringOrNull(item.nextAction),
|
||||
evidenceSummary: stringOrNull(item.evidenceSummary) ?? compactFindingEvidenceSummary(item.evidence),
|
||||
timingSourceOfTruth: stringOrNull(item.timingSourceOfTruth) ?? stringOrNull(item.expectedElapsedSource) ?? stringOrNull(item.evidenceKind),
|
||||
timingStatus: stringOrNull(item.timingStatus),
|
||||
timingAlert: item.timingAlert === true,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
@@ -1540,7 +1549,7 @@ function reportRunView(config: WebProbeSentinelServiceConfig, db: Database, view
|
||||
const findings = findingsForRun(config, db, selectedRunId, 50);
|
||||
const views = record(stored.views);
|
||||
const storedView = record(views[view]);
|
||||
const renderedText = typeof storedView.renderedText === "string" ? storedView.renderedText : view === "summary" ? renderStoredSummary(row, stored, findings) : view === "findings" ? renderStoredFindings(row, findings) : null;
|
||||
const renderedText = view === "findings" ? renderStoredFindings(row, findings) : typeof storedView.renderedText === "string" ? storedView.renderedText : view === "summary" ? renderStoredSummary(row, stored, findings) : null;
|
||||
if (renderedText === null) {
|
||||
return { ok: false, error: "report-view-not-indexed", runId: selectedRunId, view, availableViews: Object.keys(views), valuesRedacted: true };
|
||||
}
|
||||
@@ -1564,14 +1573,28 @@ function formatStoredFindingLine(item: Record<string, unknown>): string {
|
||||
const status = stringOrNull(item.rootCauseStatus);
|
||||
const nextAction = stringOrNull(item.nextAction);
|
||||
const evidence = stringOrNull(item.evidenceSummary);
|
||||
const timing = formatStoredFindingTiming(item);
|
||||
return [
|
||||
`${item.severity ?? "-"} ${code} ${title} count=${item.count ?? "-"} ${summary}`,
|
||||
timing,
|
||||
rootCause === null ? null : `rootCause=${rootCause}${status === null ? "" : ` status=${status}`}`,
|
||||
evidence === null ? null : `evidence=${evidence}`,
|
||||
nextAction === null ? null : `next=${nextAction}`,
|
||||
].filter((part) => part !== null).join(" | ");
|
||||
}
|
||||
|
||||
function formatStoredFindingTiming(item: Record<string, unknown>): string | null {
|
||||
const source = stringOrNull(item.timingSourceOfTruth);
|
||||
const status = stringOrNull(item.timingStatus);
|
||||
if (source === null && status === null) return null;
|
||||
const pieces = [
|
||||
status === null ? null : `status=${status}`,
|
||||
source === null ? null : `source=${source}`,
|
||||
item.timingAlert === true ? "alert=true" : null,
|
||||
].filter((part): part is string => part !== null);
|
||||
return `timing=${pieces.join(" ")}`;
|
||||
}
|
||||
|
||||
function renderStoredSummary(row: Record<string, unknown>, stored: Record<string, unknown>, findings: readonly Record<string, unknown>[]): string {
|
||||
const summary = record(stored.summary);
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user