feat(sentinel): restore JD01 cadence cronjob visibility

This commit is contained in:
Codex
2026-07-01 06:43:30 +00:00
parent 622804f95d
commit c7ea34f253
12 changed files with 503 additions and 28 deletions
@@ -1,4 +1,5 @@
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-27-p12-cadence-scheduler-monitor-web.
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-07-01-p15-cadence-otel.
// Responsibility: Vue monitor-web runtime for sentinel trend, timeline, detail and finding observability.
import { createApp, computed, nextTick, onMounted, ref, watch } from "./vendor/vue.esm-browser.prod.js";
@@ -156,18 +157,28 @@ createApp({
return `运行记录 · ${checkScopeRun.value?.id || "未选择"}`;
});
const cadence = computed(() => {
const apiCadence = overview.value?.cadence || {};
const intervalMs = Number(overview.value?.scheduler?.intervalMs || 0);
const latestAge = Number(overview.value?.freshness?.latestRunAgeSeconds ?? -1);
const heartbeatAge = Number(overview.value?.freshness?.schedulerHeartbeatAgeSeconds ?? -1);
const intervalSeconds = intervalMs > 0 ? Math.round(intervalMs / 1000) : 0;
const stale = intervalSeconds > 0 && latestAge > intervalSeconds * 2;
const latestAge = Number(apiCadence.latestRunAgeSeconds ?? overview.value?.freshness?.latestRunAgeSeconds ?? -1);
const heartbeatAge = Number(apiCadence.schedulerHeartbeatAgeSeconds ?? overview.value?.freshness?.schedulerHeartbeatAgeSeconds ?? -1);
const intervalSeconds = Number(apiCadence.expectedCadenceSeconds || 0) || (intervalMs > 0 ? Math.round(intervalMs / 1000) : 0);
const status = String(apiCadence.status || "");
const stale = status === "warning" || status === "blocker";
const cronJob = apiCadence.cronJob || {};
const observability = overview.value?.observability || {};
return {
intervalSeconds,
latestAge,
heartbeatAge,
status,
stale,
blocker: status === "blocker",
cronJob,
observability,
label: intervalSeconds > 0 ? `${formatDuration(intervalSeconds)} 间隔` : "未配置",
alert: stale ? `最近运行 ${formatDuration(latestAge)} 前,超过预设间隔 2 倍;按 SPEC 作为非阻塞报警展示。` : "运行新鲜度在预设窗口内",
alert: stale
? `最近运行 ${formatDuration(latestAge)} 前;状态 ${status || "warning"},阈值来自 YAML。`
: "运行新鲜度在 YAML 窗口内",
};
});
const healthChecks = computed(() => {
@@ -668,6 +679,14 @@ createApp({
<span>调度新鲜度</span>
<strong>{{ cadence.latestAge >= 0 ? formatDuration(cadence.latestAge) : "-" }}</strong>
</div>
<div class="metric" :class="{ warning: cadence.cronJob?.status !== 'ok' }">
<span>CronJob</span>
<strong>{{ cadence.cronJob?.status || "-" }}</strong>
</div>
<div class="metric" :class="{ warning: cadence.observability?.coverage === 'instrumentation-gap' }">
<span>OTel</span>
<strong>{{ cadence.observability?.coverage || "-" }}</strong>
</div>
<div class="metric">
<span>历史错误样本</span>
<strong>{{ redCount({ severityCounts: severityTotals }) }}</strong>