fix: expose sentinel run timing in monitor
This commit is contained in:
@@ -401,7 +401,7 @@ select {
|
||||
position: absolute;
|
||||
z-index: 8;
|
||||
display: grid;
|
||||
width: min(218px, calc(100% - 16px));
|
||||
width: min(286px, calc(100% - 16px));
|
||||
max-width: calc(100% - 16px);
|
||||
gap: 3px;
|
||||
transform: translate(-50%, -100%);
|
||||
|
||||
@@ -154,6 +154,8 @@ createApp({
|
||||
const redY = trendY(red);
|
||||
const warningY = trendY(warning);
|
||||
const rawTime = run.updatedAt || run.createdAt || "";
|
||||
const durationText = runDurationText(run);
|
||||
const configTimingText = runTimingConfigText(run);
|
||||
return {
|
||||
id: run.id || String(index),
|
||||
runId: run.id || "",
|
||||
@@ -170,8 +172,10 @@ createApp({
|
||||
rawTime,
|
||||
timeLabel: formatDate(rawTime),
|
||||
absoluteTime: formatAbsoluteDate(rawTime),
|
||||
durationText,
|
||||
configTimingText,
|
||||
reportSha: shortHash(run.reportJsonSha256 || run.report_json_sha256 || run.reportSha256 || ""),
|
||||
title: `${shortId(run.id)} ${formatAbsoluteDate(rawTime)} 错误 ${red} 告警 ${warning} 合计 ${total}`,
|
||||
title: `${shortId(run.id)} ${formatAbsoluteDate(rawTime)} 错误 ${red} 告警 ${warning} 合计 ${total} 运行 ${durationText} ${configTimingText}`,
|
||||
};
|
||||
}));
|
||||
const timelineRuns = computed(() => runs.value.slice(0, 16));
|
||||
@@ -463,6 +467,8 @@ createApp({
|
||||
trendTotalCount,
|
||||
trendErrorCount,
|
||||
trendWarningCount,
|
||||
runDurationText,
|
||||
runTimingConfigText,
|
||||
severityClass,
|
||||
formatDate,
|
||||
formatAbsoluteDate,
|
||||
@@ -578,6 +584,8 @@ createApp({
|
||||
<strong>{{ shortId(hoveredTrendDot.runId) }}</strong>
|
||||
<span>{{ hoveredTrendDot.absoluteTime }}</span>
|
||||
<span>状态 {{ hoveredTrendDot.status }}</span>
|
||||
<span>运行 {{ hoveredTrendDot.durationText }}</span>
|
||||
<span v-if="hoveredTrendDot.configTimingText !== '-'">配置 {{ hoveredTrendDot.configTimingText }}</span>
|
||||
<span>错误 {{ hoveredTrendDot.red }} / 告警 {{ hoveredTrendDot.warning }} / 合计 {{ hoveredTrendDot.total }}</span>
|
||||
<span v-if="hoveredTrendDot.reportSha">report {{ hoveredTrendDot.reportSha }}</span>
|
||||
</div>
|
||||
@@ -615,6 +623,7 @@ createApp({
|
||||
<span v-if="runCheckErrorCount(run) > 0" class="tag red">错误 {{ runCheckErrorCount(run) }}</span>
|
||||
<span v-if="runCheckWarningCount(run) > 0" class="tag warning">告警 {{ runCheckWarningCount(run) }}</span>
|
||||
<span v-if="runCheckAlertCount(run) === 0" class="tag healthy">正常</span>
|
||||
<span class="tag">运行 {{ runDurationText(run) }}</span>
|
||||
</span>
|
||||
</button>
|
||||
<div v-if="timelineRuns.length === 0" class="empty">暂无时间线记录</div>
|
||||
@@ -687,6 +696,7 @@ createApp({
|
||||
</span>
|
||||
<span class="row-line muted">
|
||||
<span>{{ run.status || "-" }}</span>
|
||||
<span>运行 {{ runDurationText(run) }}</span>
|
||||
<span>{{ formatDate(run.updatedAt || run.createdAt) }}</span>
|
||||
</span>
|
||||
</button>
|
||||
@@ -707,6 +717,8 @@ createApp({
|
||||
<h3>摘要</h3>
|
||||
<div class="detail-grid">
|
||||
<div class="metric"><span>状态</span><strong>{{ selectedRun.status || "-" }}</strong></div>
|
||||
<div class="metric"><span>运行分钟</span><strong>{{ runDurationText(selectedRun) }}</strong></div>
|
||||
<div class="metric"><span>配置周期</span><strong>{{ runTimingConfigText(selectedRun) }}</strong></div>
|
||||
<div class="metric"><span>监测项类型</span><strong>{{ findingCount(selectedRun) }}</strong></div>
|
||||
<div class="metric"><span>错误/告警样本</span><strong>{{ alertSampleCount(selectedRun) }}</strong></div>
|
||||
<div class="metric"><span>全部样本</span><strong>{{ findingSampleCount(selectedRun) }}</strong></div>
|
||||
@@ -1035,6 +1047,35 @@ function formatDuration(seconds) {
|
||||
return `${Math.round(value / 86400)}d`;
|
||||
}
|
||||
|
||||
function formatMinutes(value) {
|
||||
const numberValue = optionalNumber(value);
|
||||
if (numberValue === null) return "-";
|
||||
const rounded = Math.round(numberValue * 100) / 100;
|
||||
return `${Number.isInteger(rounded) ? String(rounded) : String(rounded).replace(/0+$/u, "").replace(/\.$/u, "")} 分钟`;
|
||||
}
|
||||
|
||||
function runDurationText(run) {
|
||||
const timing = run?.timing || {};
|
||||
return formatMinutes(optionalNumber(run?.runDurationMinutes, run?.durationMinutes, timing.runDurationMinutes, timing.durationMinutes));
|
||||
}
|
||||
|
||||
function runTimingConfigText(run) {
|
||||
const timing = run?.timing || {};
|
||||
const cadence = optionalNumber(run?.scenarioCadenceMinutes, timing.scenarioCadenceMinutes);
|
||||
const maxRun = optionalNumber(run?.scenarioMaxRunMinutes, timing.scenarioMaxRunMinutes);
|
||||
const scheduler = optionalNumber(run?.schedulerIntervalMinutes, timing.schedulerIntervalMinutes);
|
||||
const parts = [];
|
||||
if (cadence !== null) parts.push(`场景周期 ${formatMinutes(cadence)}`);
|
||||
if (maxRun !== null) parts.push(`上限 ${formatMinutes(maxRun)}`);
|
||||
if (scheduler !== null) parts.push(`调度 ${formatMinutes(scheduler)}`);
|
||||
return parts.length > 0 ? parts.join(" / ") : "-";
|
||||
}
|
||||
|
||||
function runTimingSourceText(run) {
|
||||
const timing = run?.timing || {};
|
||||
return safeDetailValue(run?.durationSource || timing.durationSource || timing.sourceOfTruth);
|
||||
}
|
||||
|
||||
function timeWindowLabel(value) {
|
||||
if (value === "1h") return "最近 1 小时";
|
||||
if (value === "24h") return "最近 24 小时";
|
||||
@@ -1248,6 +1289,9 @@ function detailSummaryRows(detail) {
|
||||
{ key: "run", label: "运行", value: shortId(run.id || run.runId || detail.runId) },
|
||||
{ key: "scenario", label: "场景", value: run.scenarioId || run.scenario_id || "-" },
|
||||
{ key: "status", label: "状态", value: run.status || "-" },
|
||||
{ key: "duration", label: "运行分钟", value: runDurationText(run) },
|
||||
{ key: "timing", label: "周期/上限", value: runTimingConfigText(run) },
|
||||
{ key: "durationSource", label: "计时来源", value: runTimingSourceText(run) },
|
||||
{ key: "checks", label: "监测项类型", value: String(findingCount(run)) },
|
||||
{ key: "alertSamples", label: "错误/告警样本", value: String(alertSampleCount(run)) },
|
||||
{ key: "allSamples", label: "全部样本", value: String(findingSampleCount(run)) },
|
||||
@@ -1282,3 +1326,12 @@ function number(value) {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
function optionalNumber(...values) {
|
||||
for (const value of values) {
|
||||
if (value === null || value === undefined || value === "") continue;
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) return parsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user