mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add a comparison of data to the Cardinality Explorer
(#4123)
* feat: add button "show today" to date picker * feat: add comparison with the prev day (#3967) * vmui/docs: add comparison of data to cardinality page
This commit is contained in:
parent
382a2ff649
commit
3c45256736
19 changed files with 227 additions and 59 deletions
|
@ -1,4 +1,4 @@
|
||||||
@use "../../../../styles/variables" as *;
|
@use "src/styles/variables" as *;
|
||||||
|
|
||||||
.vm-tenant-input {
|
.vm-tenant-input {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "./style.scss";
|
||||||
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import MonthsList from "./MonthsList/MonthsList";
|
import MonthsList from "./MonthsList/MonthsList";
|
||||||
|
import Button from "../../Button/Button";
|
||||||
|
|
||||||
interface DatePickerProps {
|
interface DatePickerProps {
|
||||||
date: Date | Dayjs
|
date: Date | Dayjs
|
||||||
|
@ -29,6 +30,9 @@ const Calendar: FC<DatePickerProps> = ({
|
||||||
const [viewType, setViewType] = useState<CalendarTypeView>(CalendarTypeView.days);
|
const [viewType, setViewType] = useState<CalendarTypeView>(CalendarTypeView.days);
|
||||||
const [viewDate, setViewDate] = useState(dayjs.tz(date));
|
const [viewDate, setViewDate] = useState(dayjs.tz(date));
|
||||||
const [selectDate, setSelectDate] = useState(dayjs.tz(date));
|
const [selectDate, setSelectDate] = useState(dayjs.tz(date));
|
||||||
|
|
||||||
|
const today = dayjs().startOf("day").tz();
|
||||||
|
const viewDateIsToday = today.format() === viewDate.format();
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
|
||||||
const toggleDisplayYears = () => {
|
const toggleDisplayYears = () => {
|
||||||
|
@ -44,6 +48,10 @@ const Calendar: FC<DatePickerProps> = ({
|
||||||
setSelectDate(date);
|
setSelectDate(date);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToday = () => {
|
||||||
|
setViewDate(today);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectDate.format() === dayjs.tz(date).format()) return;
|
if (selectDate.format() === dayjs.tz(date).format()) return;
|
||||||
onChange(selectDate.format(format));
|
onChange(selectDate.format(format));
|
||||||
|
@ -88,6 +96,17 @@ const Calendar: FC<DatePickerProps> = ({
|
||||||
onChangeViewDate={handleChangeViewDate}
|
onChangeViewDate={handleChangeViewDate}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!viewDateIsToday && (viewType === CalendarTypeView.days) && (
|
||||||
|
<div className="vm-calendar-footer">
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
size="small"
|
||||||
|
onClick={handleToday}
|
||||||
|
>
|
||||||
|
show today
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ interface CalendarBodyProps {
|
||||||
const weekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
const weekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||||
|
|
||||||
const CalendarBody: FC<CalendarBodyProps> = ({ viewDate, selectDate, onChangeSelectDate }) => {
|
const CalendarBody: FC<CalendarBodyProps> = ({ viewDate, selectDate, onChangeSelectDate }) => {
|
||||||
|
const format = "YYYY-MM-DD";
|
||||||
const today = dayjs().tz().startOf("day");
|
const today = dayjs().tz().startOf("day");
|
||||||
|
|
||||||
const days: (Dayjs|null)[] = useMemo(() => {
|
const days: (Dayjs|null)[] = useMemo(() => {
|
||||||
|
@ -45,10 +46,10 @@ const CalendarBody: FC<CalendarBodyProps> = ({ viewDate, selectDate, onChangeSel
|
||||||
"vm-calendar-body-cell": true,
|
"vm-calendar-body-cell": true,
|
||||||
"vm-calendar-body-cell_day": true,
|
"vm-calendar-body-cell_day": true,
|
||||||
"vm-calendar-body-cell_day_empty": !d,
|
"vm-calendar-body-cell_day_empty": !d,
|
||||||
"vm-calendar-body-cell_day_active": (d && d.toISOString()) === selectDate.startOf("day").toISOString(),
|
"vm-calendar-body-cell_day_active": (d && d.format(format)) === selectDate.format(format),
|
||||||
"vm-calendar-body-cell_day_today": (d && d.toISOString()) === today.toISOString()
|
"vm-calendar-body-cell_day_today": (d && d.format(format)) === today.format(format)
|
||||||
})}
|
})}
|
||||||
key={d ? d.toISOString() : i}
|
key={d ? d.format(format) : i}
|
||||||
onClick={createHandlerSelectDate(d)}
|
onClick={createHandlerSelectDate(d)}
|
||||||
>
|
>
|
||||||
{d && d.format("D")}
|
{d && d.format("D")}
|
||||||
|
|
|
@ -166,4 +166,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,14 @@ import "./style.scss";
|
||||||
export interface CardinalityTotalsProps {
|
export interface CardinalityTotalsProps {
|
||||||
totalSeries: number;
|
totalSeries: number;
|
||||||
totalSeriesAll: number;
|
totalSeriesAll: number;
|
||||||
|
totalSeriesPrev: number;
|
||||||
totalLabelValuePairs: number;
|
totalLabelValuePairs: number;
|
||||||
seriesCountByMetricName: TopHeapEntry[];
|
seriesCountByMetricName: TopHeapEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardinalityTotals: FC<CardinalityTotalsProps> = ({
|
const CardinalityTotals: FC<CardinalityTotalsProps> = ({
|
||||||
totalSeries,
|
totalSeries,
|
||||||
|
totalSeriesPrev,
|
||||||
totalSeriesAll,
|
totalSeriesAll,
|
||||||
seriesCountByMetricName
|
seriesCountByMetricName
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -27,11 +29,14 @@ const CardinalityTotals: FC<CardinalityTotalsProps> = ({
|
||||||
const isMetric = /__name__/.test(match || "");
|
const isMetric = /__name__/.test(match || "");
|
||||||
|
|
||||||
const progress = seriesCountByMetricName[0]?.value / totalSeriesAll * 100;
|
const progress = seriesCountByMetricName[0]?.value / totalSeriesAll * 100;
|
||||||
|
const diff = totalSeries - totalSeriesPrev;
|
||||||
|
const dynamic = Math.abs(diff) / totalSeriesPrev * 100;
|
||||||
|
|
||||||
const totals = [
|
const totals = [
|
||||||
{
|
{
|
||||||
title: "Total series",
|
title: "Total series",
|
||||||
value: totalSeries.toLocaleString("en-US"),
|
value: totalSeries.toLocaleString("en-US"),
|
||||||
|
dynamic: !totalSeries || !totalSeriesPrev ? "" : `${dynamic.toFixed(2)}%`,
|
||||||
display: !focusLabel,
|
display: !focusLabel,
|
||||||
info: `The total number of active time series.
|
info: `The total number of active time series.
|
||||||
A time series is uniquely identified by its name plus a set of its labels.
|
A time series is uniquely identified by its name plus a set of its labels.
|
||||||
|
@ -57,20 +62,33 @@ const CardinalityTotals: FC<CardinalityTotalsProps> = ({
|
||||||
"vm-cardinality-totals_mobile": isMobile
|
"vm-cardinality-totals_mobile": isMobile
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{totals.map(({ title, value, info }) => (
|
{totals.map(({ title, value, info, dynamic }) => (
|
||||||
<div
|
<div
|
||||||
className="vm-cardinality-totals-card"
|
className="vm-cardinality-totals-card"
|
||||||
key={title}
|
key={title}
|
||||||
>
|
>
|
||||||
<div className="vm-cardinality-totals-card-header">
|
<h4 className="vm-cardinality-totals-card__title">
|
||||||
|
{title}
|
||||||
{info && (
|
{info && (
|
||||||
<Tooltip title={<p className="vm-cardinality-totals-card-header__tooltip">{info}</p>}>
|
<Tooltip title={<p className="vm-cardinality-totals-card__tooltip">{info}</p>}>
|
||||||
<div className="vm-cardinality-totals-card-header__info-icon"><InfoIcon/></div>
|
<div className="vm-cardinality-totals-card__info-icon"><InfoIcon/></div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<h4 className="vm-cardinality-totals-card-header__title">{title}</h4>
|
</h4>
|
||||||
</div>
|
|
||||||
<span className="vm-cardinality-totals-card__value">{value}</span>
|
<span className="vm-cardinality-totals-card__value">{value}</span>
|
||||||
|
{!!dynamic && (
|
||||||
|
<Tooltip title={`in relation to the previous day: ${totalSeriesPrev.toLocaleString("en-US")}`}>
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
"vm-dynamic-number": true,
|
||||||
|
"vm-dynamic-number_positive vm-dynamic-number_down": diff < 0,
|
||||||
|
"vm-dynamic-number_negative vm-dynamic-number_up": diff > 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{dynamic}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,25 +5,20 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: $padding-global;
|
gap: $padding-medium;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
&_mobile {
|
&_mobile {
|
||||||
gap: $padding-small;
|
gap: $padding-global;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-card {
|
&-card {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 4px;
|
gap: $padding-small 4px;
|
||||||
|
|
||||||
&-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 4px;
|
|
||||||
|
|
||||||
&__info-icon {
|
&__info-icon {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
@ -34,27 +29,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-weight: bold;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 4px;
|
||||||
|
grid-column: 1/-1;
|
||||||
color: $color-text;
|
color: $color-text;
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ':';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__tooltip {
|
&__tooltip {
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
padding: $padding-small;
|
padding: $padding-small;
|
||||||
line-height: 130%;
|
line-height: $font-size;
|
||||||
font-size: $font-size;
|
font-size: $font-size;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__value {
|
&__value {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
font-size: $font-size-medium;
|
font-size: $font-size-large;
|
||||||
|
line-height: $font-size;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ interface MetricsProperties {
|
||||||
tabs: string[];
|
tabs: string[];
|
||||||
chartContainer: MutableRef<HTMLDivElement> | undefined;
|
chartContainer: MutableRef<HTMLDivElement> | undefined;
|
||||||
totalSeries: number,
|
totalSeries: number,
|
||||||
|
totalSeriesPrev: number,
|
||||||
sectionTitle: string;
|
sectionTitle: string;
|
||||||
tip?: string;
|
tip?: string;
|
||||||
tableHeaderCells: HeadCell[];
|
tableHeaderCells: HeadCell[];
|
||||||
|
@ -28,6 +29,7 @@ const MetricsContent: FC<MetricsProperties> = ({
|
||||||
tabs: tabsProps = [],
|
tabs: tabsProps = [],
|
||||||
chartContainer,
|
chartContainer,
|
||||||
totalSeries,
|
totalSeries,
|
||||||
|
totalSeriesPrev,
|
||||||
onActionClick,
|
onActionClick,
|
||||||
sectionTitle,
|
sectionTitle,
|
||||||
tip,
|
tip,
|
||||||
|
@ -40,6 +42,7 @@ const MetricsContent: FC<MetricsProperties> = ({
|
||||||
<TableCells
|
<TableCells
|
||||||
row={row}
|
row={row}
|
||||||
totalSeries={totalSeries}
|
totalSeries={totalSeries}
|
||||||
|
totalSeriesPrev={totalSeriesPrev}
|
||||||
onActionClick={onActionClick}
|
onActionClick={onActionClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,7 +26,7 @@ const EnhancedTable: FC<TableProps> = ({
|
||||||
const sortedData = stableSort(rows, getComparator(order, orderBy));
|
const sortedData = stableSort(rows, getComparator(order, orderBy));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="vm-table">
|
<table className="vm-table vm-cardinality-panel-table">
|
||||||
<EnhancedTableHead
|
<EnhancedTableHead
|
||||||
order={order}
|
order={order}
|
||||||
orderBy={orderBy}
|
orderBy={orderBy}
|
||||||
|
|
|
@ -4,15 +4,27 @@ import LineProgress from "../../../../components/Main/LineProgress/LineProgress"
|
||||||
import { PlayCircleOutlineIcon } from "../../../../components/Main/Icons";
|
import { PlayCircleOutlineIcon } from "../../../../components/Main/Icons";
|
||||||
import Button from "../../../../components/Main/Button/Button";
|
import Button from "../../../../components/Main/Button/Button";
|
||||||
import Tooltip from "../../../../components/Main/Tooltip/Tooltip";
|
import Tooltip from "../../../../components/Main/Tooltip/Tooltip";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
interface CardinalityTableCells {
|
interface CardinalityTableCells {
|
||||||
row: Data,
|
row: Data,
|
||||||
totalSeries: number;
|
totalSeries: number;
|
||||||
|
totalSeriesPrev: number;
|
||||||
onActionClick: (name: string) => void;
|
onActionClick: (name: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableCells: FC<CardinalityTableCells> = ({ row, totalSeries, onActionClick }) => {
|
const TableCells: FC<CardinalityTableCells> = ({
|
||||||
|
row,
|
||||||
|
totalSeries,
|
||||||
|
totalSeriesPrev,
|
||||||
|
onActionClick
|
||||||
|
}) => {
|
||||||
const progress = totalSeries > 0 ? row.value / totalSeries * 100 : -1;
|
const progress = totalSeries > 0 ? row.value / totalSeries * 100 : -1;
|
||||||
|
const progressPrev = totalSeriesPrev > 0 ? row.valuePrev / totalSeriesPrev * 100 : -1;
|
||||||
|
const hasProgresses = [progress, progressPrev].some(p => p === -1);
|
||||||
|
|
||||||
|
const diffPercent = progress - progressPrev;
|
||||||
|
const relationPrevDay = hasProgresses ? "" : `${diffPercent.toFixed(2)}%`;
|
||||||
|
|
||||||
const handleActionClick = () => {
|
const handleActionClick = () => {
|
||||||
onActionClick(row.name);
|
onActionClick(row.name);
|
||||||
|
@ -35,13 +47,42 @@ const TableCells: FC<CardinalityTableCells> = ({ row, totalSeries, onActionClick
|
||||||
key={row.value}
|
key={row.value}
|
||||||
>
|
>
|
||||||
{row.value}
|
{row.value}
|
||||||
|
|
||||||
|
{!!row.diff && (
|
||||||
|
<Tooltip title={`in relation to the previous day: ${row.valuePrev}`}>
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
"vm-dynamic-number": true,
|
||||||
|
"vm-dynamic-number_positive": row.diff < 0,
|
||||||
|
"vm-dynamic-number_negative": row.diff > 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{row.diff > 0 ? "+" : ""}{row.diff}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
{progress > 0 && (
|
{progress > 0 && (
|
||||||
<td
|
<td
|
||||||
className="vm-table-cell"
|
className="vm-table-cell"
|
||||||
key={row.progressValue}
|
key={row.progressValue}
|
||||||
>
|
>
|
||||||
|
<div className="vm-cardinality-panel-table__progress">
|
||||||
<LineProgress value={progress}/>
|
<LineProgress value={progress}/>
|
||||||
|
{relationPrevDay && (
|
||||||
|
<Tooltip title={"in relation to the previous day"}>
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
"vm-dynamic-number": true,
|
||||||
|
"vm-dynamic-number_positive vm-dynamic-number_down": diffPercent < 0,
|
||||||
|
"vm-dynamic-number_negative vm-dynamic-number_up": diffPercent > 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{relationPrevDay}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<td
|
<td
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function EnhancedTableHead(props: EnhancedHeaderTableProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<thead className="vm-table-header">
|
<thead className="vm-table-header vm-cardinality-panel-table__header">
|
||||||
<tr className="vm-table__row vm-table__row_header">
|
<tr className="vm-table__row vm-table__row_header">
|
||||||
{headerCells.map((headCell) => (
|
{headerCells.map((headCell) => (
|
||||||
<th
|
<th
|
||||||
|
|
|
@ -28,6 +28,8 @@ export interface TableProps {
|
||||||
export interface Data {
|
export interface Data {
|
||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
|
diff: number;
|
||||||
|
valuePrev: number;
|
||||||
progressValue: number;
|
progressValue: number;
|
||||||
actions: string;
|
actions: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ export default class AppConfigurator {
|
||||||
get defaultTSDBStatus(): TSDBStatus {
|
get defaultTSDBStatus(): TSDBStatus {
|
||||||
return {
|
return {
|
||||||
totalSeries: 0,
|
totalSeries: 0,
|
||||||
totalLabelValuePairs: 0,
|
totalSeriesPrev: 0,
|
||||||
totalSeriesByAll: 0,
|
totalSeriesByAll: 0,
|
||||||
|
totalLabelValuePairs: 0,
|
||||||
seriesCountByMetricName: [],
|
seriesCountByMetricName: [],
|
||||||
seriesCountByLabelName: [],
|
seriesCountByLabelName: [],
|
||||||
seriesCountByFocusLabelValue: [],
|
seriesCountByFocusLabelValue: [],
|
||||||
|
@ -142,11 +143,11 @@ export default class AppConfigurator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
totalSeries(keyName: string): number {
|
totalSeries(keyName: string, prev = false): number {
|
||||||
if (keyName === "labelValueCountByLabelName") {
|
if (keyName === "labelValueCountByLabelName") {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return this.tsdbStatus.totalSeries;
|
return prev ? this.tsdbStatus.totalSeriesPrev : this.tsdbStatus.totalSeries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,24 +33,51 @@ export const useFetchQuery = (): {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setTSDBStatus(appConfigurator.defaultTSDBStatus);
|
setTSDBStatus(appConfigurator.defaultTSDBStatus);
|
||||||
|
|
||||||
const defaultParams = { date: requestParams.date, topN: 0, match: "", focusLabel: "" } as CardinalityRequestsParams;
|
const totalParams = {
|
||||||
const url = getCardinalityInfo(serverUrl, requestParams);
|
date: requestParams.date,
|
||||||
const urlDefault = getCardinalityInfo(serverUrl, defaultParams);
|
topN: 0,
|
||||||
|
match: "",
|
||||||
|
focusLabel: ""
|
||||||
|
} as CardinalityRequestsParams;
|
||||||
|
|
||||||
|
const prevDayParams = {
|
||||||
|
...requestParams,
|
||||||
|
date: dayjs(requestParams.date).subtract(1, "day").tz().format(DATE_FORMAT),
|
||||||
|
} as CardinalityRequestsParams;
|
||||||
|
|
||||||
|
|
||||||
|
const urlBase = getCardinalityInfo(serverUrl, requestParams);
|
||||||
|
const urlPrev = getCardinalityInfo(serverUrl, prevDayParams);
|
||||||
|
const uslTotal = getCardinalityInfo(serverUrl, totalParams);
|
||||||
|
const urls = [urlBase, urlPrev, uslTotal];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url);
|
const responses = await Promise.all(urls.map(url => fetch(url)));
|
||||||
const resp = await response.json();
|
const [resp, respPrev, respTotals] = await Promise.all(responses.map(resp => resp.json()));
|
||||||
const responseTotal = await fetch(urlDefault);
|
if (responses[0].ok) {
|
||||||
const respTotals = await responseTotal.json();
|
const { data: dataTotal } = respTotals;
|
||||||
if (response.ok) {
|
const prevResult = { ...respPrev.data } as TSDBStatus;
|
||||||
const { data } = resp;
|
const result = { ...resp.data } as TSDBStatus;
|
||||||
const { totalSeries } = respTotals.data;
|
result.totalSeriesByAll = dataTotal?.totalSeries;
|
||||||
const result = { ...data } as TSDBStatus;
|
result.totalSeriesPrev = prevResult?.totalSeries;
|
||||||
result.totalSeriesByAll = totalSeries;
|
|
||||||
|
|
||||||
const name = match?.replace(/[{}"]/g, "");
|
const name = match?.replace(/[{}"]/g, "");
|
||||||
result.seriesCountByLabelValuePair = result.seriesCountByLabelValuePair.filter(s => s.name !== name);
|
result.seriesCountByLabelValuePair = result.seriesCountByLabelValuePair.filter(s => s.name !== name);
|
||||||
|
|
||||||
|
Object.keys(result).forEach(k => {
|
||||||
|
const key = k as keyof TSDBStatus;
|
||||||
|
const entries = result[key];
|
||||||
|
const prevEntries = prevResult[key];
|
||||||
|
|
||||||
|
if (Array.isArray(entries) && Array.isArray(prevEntries)) {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const valuePrev = prevEntries.find(prevEntry => prevEntry.name === entry.name)?.value;
|
||||||
|
entry.diff = valuePrev ? entry.value - valuePrev : 0;
|
||||||
|
entry.valuePrev = valuePrev || 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setTSDBStatus(result);
|
setTSDBStatus(result);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -55,6 +55,7 @@ const CardinalityPanel: FC = () => {
|
||||||
{isLoading && <Spinner message={spinnerMessage}/>}
|
{isLoading && <Spinner message={spinnerMessage}/>}
|
||||||
<CardinalityConfigurator
|
<CardinalityConfigurator
|
||||||
totalSeries={tsdbStatusData.totalSeries}
|
totalSeries={tsdbStatusData.totalSeries}
|
||||||
|
totalSeriesPrev={tsdbStatusData.totalSeriesPrev}
|
||||||
totalSeriesAll={tsdbStatusData.totalSeriesByAll}
|
totalSeriesAll={tsdbStatusData.totalSeriesByAll}
|
||||||
totalLabelValuePairs={tsdbStatusData.totalLabelValuePairs}
|
totalLabelValuePairs={tsdbStatusData.totalLabelValuePairs}
|
||||||
seriesCountByMetricName={tsdbStatusData.seriesCountByMetricName}
|
seriesCountByMetricName={tsdbStatusData.seriesCountByMetricName}
|
||||||
|
@ -80,6 +81,7 @@ const CardinalityPanel: FC = () => {
|
||||||
onActionClick={handleFilterClick(keyName)}
|
onActionClick={handleFilterClick(keyName)}
|
||||||
tabs={defaultState.tabs[keyName as keyof Tabs]}
|
tabs={defaultState.tabs[keyName as keyof Tabs]}
|
||||||
chartContainer={defaultState.containerRefs[keyName as keyof Containers<HTMLDivElement>]}
|
chartContainer={defaultState.containerRefs[keyName as keyof Containers<HTMLDivElement>]}
|
||||||
|
totalSeriesPrev={appConfigurator.totalSeries(keyName, true)}
|
||||||
totalSeries={appConfigurator.totalSeries(keyName)}
|
totalSeries={appConfigurator.totalSeries(keyName)}
|
||||||
tableHeaderCells={tablesHeaders[keyName]}
|
tableHeaderCells={tablesHeaders[keyName]}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,4 +18,25 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-table {
|
||||||
|
&__header {
|
||||||
|
th:first-child {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:first-child) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&__progress {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(200px, 1fr) 70px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: $padding-small;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ export interface TSDBStatus {
|
||||||
totalSeries: number;
|
totalSeries: number;
|
||||||
totalLabelValuePairs: number;
|
totalLabelValuePairs: number;
|
||||||
totalSeriesByAll: number,
|
totalSeriesByAll: number,
|
||||||
|
totalSeriesPrev: number,
|
||||||
seriesCountByMetricName: TopHeapEntry[];
|
seriesCountByMetricName: TopHeapEntry[];
|
||||||
seriesCountByLabelName: TopHeapEntry[];
|
seriesCountByLabelName: TopHeapEntry[];
|
||||||
seriesCountByFocusLabelValue: TopHeapEntry[];
|
seriesCountByFocusLabelValue: TopHeapEntry[];
|
||||||
|
@ -14,6 +15,8 @@ export interface TSDBStatus {
|
||||||
export interface TopHeapEntry {
|
export interface TopHeapEntry {
|
||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
|
diff: number;
|
||||||
|
valuePrev: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueryUpdaterArgs {
|
interface QueryUpdaterArgs {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
@use "src/styles/variables" as *;
|
||||||
|
|
||||||
|
.vm-dynamic-number {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
color: $color-text-disabled;
|
||||||
|
|
||||||
|
&_positive {
|
||||||
|
color: $color-success;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_negative {
|
||||||
|
color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_down {
|
||||||
|
&:before {
|
||||||
|
content: "↓";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_up {
|
||||||
|
&:before {
|
||||||
|
content: "↑";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
@forward "./components/sectionheader";
|
@forward "./components/sectionheader";
|
||||||
@forward "./components/table";
|
@forward "./components/table";
|
||||||
@forward "./components/link";
|
@forward "./components/link";
|
||||||
|
@forward "./components/dynamic-number";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* base palette */
|
/* base palette */
|
||||||
|
|
|
@ -25,6 +25,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* FEATURE: introduce `-http.maxConcurrentRequests` command-line flag to protect VM components from resource exhaustion during unexpected spikes of HTTP requests. By default, the new flag's value is set to 0 which means no limits are applied.
|
* FEATURE: introduce `-http.maxConcurrentRequests` command-line flag to protect VM components from resource exhaustion during unexpected spikes of HTTP requests. By default, the new flag's value is set to 0 which means no limits are applied.
|
||||||
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support for the different time formats for `--vm-native-filter-time-start` and `--vm-native-filter-time-end` flags if the native binary protocol is used for migration. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091).
|
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support for the different time formats for `--vm-native-filter-time-start` and `--vm-native-filter-time-end` flags if the native binary protocol is used for migration. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): integrate WITH template playground. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): integrate WITH template playground. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811).
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add a comparison of data from the previous day with data from the current day to the `Cardinality Explorer`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3967).
|
||||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to filter incoming requests by IP. See [these docs](https://docs.victoriametrics.com/vmauth.html#ip-filters) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3491).
|
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to filter incoming requests by IP. See [these docs](https://docs.victoriametrics.com/vmauth.html#ip-filters) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3491).
|
||||||
|
|
||||||
* BUGFIX: reduce the probability of sudden increase in the number of small parts on systems with small number of CPU cores.
|
* BUGFIX: reduce the probability of sudden increase in the number of small parts on systems with small number of CPU cores.
|
||||||
|
|
Loading…
Reference in a new issue