Streamline missing data warnings, review logging

This commit is contained in:
Christoph Kluge
2026-01-29 15:17:33 +01:00
parent 7101d2bb3b
commit f26cabbdf1
11 changed files with 147 additions and 88 deletions

View File

@@ -342,7 +342,7 @@
<b>Disabled Metric</b>
</CardHeader>
<CardBody>
<p>Metric <b>{item.metric}</b> is disabled for subcluster <b>{$initq.data.job.subCluster}</b>.</p>
<p>Metric <b>{item.metric}</b> is disabled for cluster <b>{$initq.data.job.cluster}:{$initq.data.job.subCluster}</b>.</p>
<p class="mb-1">To remove this card, open metric selection and press "Close and Apply".</p>
</CardBody>
</Card>
@@ -352,7 +352,8 @@
<b>Missing Metric</b>
</CardHeader>
<CardBody>
<p class="mb-1">No dataset returned for <b>{item.metric}</b>.</p>
<p>No dataset returned for <b>{item.metric}</b>.</p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{$initq.data.job.cluster}</b>.</p>
</CardBody>
</Card>
{/if}

View File

@@ -22,6 +22,8 @@
Icon,
Spinner,
Card,
CardHeader,
CardBody
} from "@sveltestrap/sveltestrap";
import {
queryStore,
@@ -254,12 +256,15 @@
></Card
>
{:else}
<Card
style="margin-left: 2rem;margin-right: 2rem;"
body
color="warning"
>No dataset returned for <code>{item.name}</code></Card
>
<Card color="warning" class="mx-2">
<CardHeader class="mb-0">
<b>Missing Metric</b>
</CardHeader>
<CardBody>
<p>No dataset returned for <b>{item.name}</b>.</p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
</CardBody>
</Card>
{/if}
{/snippet}

View File

@@ -229,7 +229,10 @@
></Card
>
{:else}
<Card body color="warning">No dataset returned</Card>
<Card body class="mx-2" color="warning">
<p>No dataset returned for <b>{metrics[i]}</b></p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{job.cluster}</b>.</p>
</Card>
{/if}
</td>
{/each}

View File

@@ -27,7 +27,7 @@
import uPlot from "uplot";
import { formatNumber, formatDurationTime } from "../units.js";
import { getContext, onMount, onDestroy } from "svelte";
import { Card } from "@sveltestrap/sveltestrap";
import { Card, CardBody, CardHeader } from "@sveltestrap/sveltestrap";
/* Svelte 5 Props */
let {
@@ -633,7 +633,13 @@
style="background-color: {backgroundColor()};" class={forNode ? 'py-2 rounded' : 'rounded'}
></div>
{:else}
<Card body color="warning" class="mx-4"
>Cannot render plot: No series data returned for <code>{metric}</code></Card
>
<Card color="warning" class={forNode ? 'mx-2' : 'mt-2'}>
<CardHeader class="mb-0">
<b>Empty Metric</b>
</CardHeader>
<CardBody>
<p>Cannot render plot for <b>{metric}</b>.</p>
<p class="mb-1">Metric found but returned without timeseries data.</p>
</CardBody>
</Card>
{/if}

View File

@@ -55,6 +55,7 @@
function setupAvailable(data) {
let pendingAvailable = {};
if (data) {
// Returns Only For Available Metrics
for (let d of data) {
if (!pendingAvailable[d.name]) {
pendingAvailable[d.name] = [d.scope]
@@ -90,13 +91,16 @@
pendingTableData[host] = {};
};
for (const metric of sm) {
if (!pendingTableData[host][metric]) {
pendingTableData[host][metric] = {};
};
for (const scope of as[metric]) {
pendingTableData[host][metric][scope] = js.find((d) => d.name == metric && d.scope == scope)
?.stats.filter((st) => st.hostname == host && st.data != null)
?.sort((a, b) => a.id - b.id) || []
// Only Returned, Available Metrics
if (as[metric]) {
if (!pendingTableData[host][metric]) {
pendingTableData[host][metric] = {};
};
for (const scope of as[metric]) {
pendingTableData[host][metric][scope] = js.find((d) => d.name == metric && d.scope == scope)
?.stats.filter((st) => st.hostname == host && st.data != null)
?.sort((a, b) => a.id - b.id) || []
};
};
};
};
@@ -136,40 +140,56 @@
<th></th>
{#each selectedMetrics as metric}
<!-- To Match Row-2 Header Field Count-->
<th colspan={selectedScopes[metric] == "node" ? 3 : 4}>
<InputGroup>
<InputGroupText>
{metric}
</InputGroupText>
<Input type="select" bind:value={selectedScopes[metric]} disabled={availableScopes[metric]?.length === 1}>
{#each (availableScopes[metric] || []) as scope}
<option value={scope}>{scope}</option>
{/each}
</Input>
</InputGroup>
</th>
{#if availableScopes[metric]}
<th colspan={selectedScopes[metric] == "node" ? 3 : 4}>
<InputGroup>
<InputGroupText>
{metric}
</InputGroupText>
<Input type="select" bind:value={selectedScopes[metric]} disabled={availableScopes[metric]?.length === 1}>
{#each (availableScopes[metric] || []) as scope}
<option value={scope}>{scope}</option>
{/each}
</Input>
</InputGroup>
</th>
{:else}
<th>
<InputGroup>
<InputGroupText>
{metric}
</InputGroupText>
</InputGroup>
</th>
{/if}
{/each}
</tr>
<!-- Header Row 2: Fields -->
<tr>
<th>Node</th>
{#each selectedMetrics as metric}
{#if selectedScopes[metric] != "node"}
<th>Id</th>
{/if}
{#each ["min", "avg", "max"] as stat}
<th onclick={() => sortBy(metric, stat)}>
{stat}
{#if selectedScopes[metric] == "node"}
<Icon
name="caret-{sorting[metric][stat].dir}{sorting[metric][stat]
.active
? '-fill'
: ''}"
/>
{/if}
{#if availableScopes[metric]}
{#if selectedScopes[metric] != "node"}
<th>Id</th>
{/if}
{#each ["min", "avg", "max"] as stat}
<th onclick={() => sortBy(metric, stat)}>
{stat}
{#if selectedScopes[metric] == "node"}
<Icon
name="caret-{sorting[metric][stat].dir}{sorting[metric][stat]
.active
? '-fill'
: ''}"
/>
{/if}
</th>
{/each}
{:else}
<th class="table-warning">
Missing Metric
</th>
{/each}
{/if}
{/each}
</tr>
</thead>
@@ -178,10 +198,17 @@
<tr>
<th scope="col">{host}</th>
{#each selectedMetrics as metric (metric)}
<StatsTableEntry
data={tableData[host][metric][selectedScopes[metric]]}
scope={selectedScopes[metric]}
/>
{#if tableData[host][metric]}
<StatsTableEntry
data={tableData[host][metric][selectedScopes[metric]]}
scope={selectedScopes[metric]}
/>
{:else}
<td class="table-warning" style="max-width:10rem;">
<p>No dataset returned for <b>{metric}</b>.</p>
<p>Metric was not found in metric store for cluster.</p>
</td>
{/if}
{/each}
</tr>
{/each}

View File

@@ -14,7 +14,7 @@
<script>
import { getContext } from "svelte";
import { queryStore, gql, getContextClient } from "@urql/svelte";
import { Row, Col, Card, Spinner, Badge } from "@sveltestrap/sveltestrap";
import { Row, Col, Card, CardHeader, CardBody, Spinner, Badge } from "@sveltestrap/sveltestrap";
import { checkMetricDisabled } from "../generic/utils.js";
import MetricPlot from "../generic/plots/MetricPlot.svelte";
@@ -189,4 +189,16 @@
{/each}
{/key}
</Row>
{:else}
<Row>
<Card color="warning">
<CardHeader class="mb-0">
<b>Missing Metric</b>
</CardHeader>
<CardBody>
<p>No dataset returned for <b>{selectedMetric}</b>.</p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
</CardBody>
</Card>
</Row>
{/if}

View File

@@ -171,13 +171,18 @@
{#key metricData}
<td>
{#if metricData?.disabled}
<Card body class="mx-3" color="info"
>Metric disabled for subcluster <code
>{metricData?.data?.name ? metricData.data.name : `Metric Index ${i}`}:{nodeData.subCluster}</code
<Card body class="mx-2" color="info"
>Metric <b>{selectedMetrics[i]}</b> disabled for subcluster <code
>{nodeData.subCluster}</code
></Card
>
{:else if !metricData?.data}
<Card body class="mx-2" color="warning">
<p>No dataset returned for <b>{selectedMetrics[i]}</b></p>
<p class="mb-1">Metric was not found in metric store for cluster <b>{cluster}</b>.</p>
</Card>
{:else if !metricData?.data?.name}
<Card body class="mx-3" color="warning"
<Card body class="mx-2" color="warning"
>Metric without name for subcluster <code
>{`Metric Index ${i}`}:{nodeData.subCluster}</code
></Card