mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add last/max/avg values (#3789)
* feat: add last/max/avg values (#3706) * fix: change filter exclude values * app/vmui: wip - improve the visualization for avg/max/last values - make getAvgFromArray() function resilient against inf/undefined/nil - export getLastFromArray() function, which is resilient against inf/undefined/nil - run `make vmui-update` --------- Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
114c14febf
commit
8afc0aef8d
19 changed files with 111 additions and 48 deletions
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.a6dcf95f.css",
|
"main.css": "./static/css/main.f22be84b.css",
|
||||||
"main.js": "./static/js/main.83e96a22.js",
|
"main.js": "./static/js/main.eca4a392.js",
|
||||||
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
||||||
"static/media/Lato-Regular.ttf": "./static/media/Lato-Regular.d714fec1633b69a9c2e9.ttf",
|
"static/media/Lato-Regular.ttf": "./static/media/Lato-Regular.d714fec1633b69a9c2e9.ttf",
|
||||||
"static/media/Lato-Bold.ttf": "./static/media/Lato-Bold.32360ba4b57802daa4d6.ttf",
|
"static/media/Lato-Bold.ttf": "./static/media/Lato-Bold.32360ba4b57802daa4d6.ttf",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.a6dcf95f.css",
|
"static/css/main.f22be84b.css",
|
||||||
"static/js/main.83e96a22.js"
|
"static/js/main.eca4a392.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.83e96a22.js"></script><link href="./static/css/main.a6dcf95f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.eca4a392.js"></script><link href="./static/css/main.f22be84b.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
import React, { FC, useEffect, useMemo, useRef, useState } from "preact/compat";
|
import React, { FC, useEffect, useMemo, useRef, useState } from "preact/compat";
|
||||||
import uPlot, { Series } from "uplot";
|
import uPlot from "uplot";
|
||||||
import { MetricResult } from "../../../api/types";
|
import { MetricResult } from "../../../api/types";
|
||||||
import { formatPrettyNumber } from "../../../utils/uplot/helpers";
|
import { formatPrettyNumber } from "../../../utils/uplot/helpers";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
@ -11,12 +11,13 @@ import { CloseIcon, DragIcon } from "../../Main/Icons";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { MouseEvent as ReactMouseEvent } from "react";
|
import { MouseEvent as ReactMouseEvent } from "react";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
|
import { SeriesItem } from "../../../utils/uplot/series";
|
||||||
|
|
||||||
export interface ChartTooltipProps {
|
export interface ChartTooltipProps {
|
||||||
id: string,
|
id: string,
|
||||||
u: uPlot,
|
u: uPlot,
|
||||||
metrics: MetricResult[],
|
metrics: MetricResult[],
|
||||||
series: Series[],
|
series: SeriesItem[],
|
||||||
yRange: number[];
|
yRange: number[];
|
||||||
unit?: string,
|
unit?: string,
|
||||||
isSticky?: boolean,
|
isSticky?: boolean,
|
||||||
|
@ -55,6 +56,8 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
||||||
|
|
||||||
const color = series[seriesIdx]?.stroke+"";
|
const color = series[seriesIdx]?.stroke+"";
|
||||||
|
|
||||||
|
const calculations = series[seriesIdx]?.calculations || {};
|
||||||
|
|
||||||
const groups = new Set(metrics.map(m => m.group));
|
const groups = new Set(metrics.map(m => m.group));
|
||||||
const showQueryNum = groups.size > 1;
|
const showQueryNum = groups.size > 1;
|
||||||
const group = metrics[seriesIdx-1]?.group || 0;
|
const group = metrics[seriesIdx-1]?.group || 0;
|
||||||
|
@ -175,9 +178,14 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
||||||
style={{ background: color }}
|
style={{ background: color }}
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
{metricName}:
|
{calculations.last !== undefined && (
|
||||||
<b className="vm-chart-tooltip-data__value">{valueFormat}</b>
|
<div>
|
||||||
{unit}
|
avg:<b>{calculations.avg}</b>,
|
||||||
|
max:<b>{calculations.max}</b>,
|
||||||
|
last:<b>{calculations.last}</b>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{metricName}:<b>{valueFormat}{unit}</b>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{!!fields.length && (
|
{!!fields.length && (
|
||||||
|
|
|
@ -61,20 +61,9 @@ $chart-tooltip-y: -1 * ($padding-small + $chart-tooltip-half-icon);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
|
|
||||||
&__value {
|
|
||||||
padding: 4px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__marker {
|
&__marker {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-info {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 4px;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ interface LegendItemProps {
|
||||||
const LegendItem: FC<LegendItemProps> = ({ legend, onChange }) => {
|
const LegendItem: FC<LegendItemProps> = ({ legend, onChange }) => {
|
||||||
const [copiedValue, setCopiedValue] = useState("");
|
const [copiedValue, setCopiedValue] = useState("");
|
||||||
const freeFormFields = useMemo(() => getFreeFields(legend), [legend]);
|
const freeFormFields = useMemo(() => getFreeFields(legend), [legend]);
|
||||||
|
const calculations = legend.calculations;
|
||||||
|
|
||||||
const handleClickFreeField = async (val: string, id: string) => {
|
const handleClickFreeField = async (val: string, id: string) => {
|
||||||
await navigator.clipboard.writeText(val);
|
await navigator.clipboard.writeText(val);
|
||||||
|
@ -30,11 +31,11 @@ const LegendItem: FC<LegendItemProps> = ({ legend, onChange }) => {
|
||||||
handleClickFreeField(freeField, id);
|
handleClickFreeField(freeField, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"vm-legend-item": true,
|
"vm-legend-item": true,
|
||||||
|
"vm-legend-row": true,
|
||||||
"vm-legend-item_hide": !legend.checked,
|
"vm-legend-item_hide": !legend.checked,
|
||||||
})}
|
})}
|
||||||
onClick={createHandlerClick(legend)}
|
onClick={createHandlerClick(legend)}
|
||||||
|
@ -70,6 +71,11 @@ const LegendItem: FC<LegendItemProps> = ({ legend, onChange }) => {
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
{calculations.last !== undefined && (
|
||||||
|
<div className="vm-legend-item-values">
|
||||||
|
avg:{calculations.avg}, max:{calculations.max}, last:{calculations.last}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
background-color: $color-background-block;
|
background-color: $color-background-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.2s ease;
|
transition: 0.2s ease;
|
||||||
|
margin-bottom: $padding-small;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
@ -33,10 +34,11 @@
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__free-fields {
|
&__free-fields {
|
||||||
padding: 3px;
|
padding: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -48,4 +50,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-values {
|
||||||
|
grid-column: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $padding-small;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,13 @@
|
||||||
|
|
||||||
&-group {
|
&-group {
|
||||||
min-width: 23%;
|
min-width: 23%;
|
||||||
|
width: 100%;
|
||||||
margin: 0 $padding-global $padding-global 0;
|
margin: 0 $padding-global $padding-global 0;
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 $padding-small $padding-small;
|
padding: $padding-small;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
border-bottom: $border-divider;
|
border-bottom: $border-divider;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import classNames from "classnames";
|
||||||
import ChartTooltip, { ChartTooltipProps } from "../ChartTooltip/ChartTooltip";
|
import ChartTooltip, { ChartTooltipProps } from "../ChartTooltip/ChartTooltip";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useAppState } from "../../../state/common/StateContext";
|
import { useAppState } from "../../../state/common/StateContext";
|
||||||
|
import { SeriesItem } from "../../../utils/uplot/series";
|
||||||
|
|
||||||
export interface LineChartProps {
|
export interface LineChartProps {
|
||||||
metrics: MetricResult[];
|
metrics: MetricResult[];
|
||||||
|
@ -316,7 +317,7 @@ const LineChart: FC<LineChartProps> = ({
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
unit={unit}
|
unit={unit}
|
||||||
u={uPlotInst}
|
u={uPlotInst}
|
||||||
series={series}
|
series={series as SeriesItem[]}
|
||||||
metrics={metrics}
|
metrics={metrics}
|
||||||
yRange={yRange}
|
yRange={yRange}
|
||||||
tooltipIdx={tooltipIdx}
|
tooltipIdx={tooltipIdx}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { getAvgFromArray, getMaxFromArray, getMinFromArray } from "../../../util
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
|
import { promValueToNumber } from "../../../utils/metric";
|
||||||
|
|
||||||
export interface GraphViewProps {
|
export interface GraphViewProps {
|
||||||
data?: MetricResult[];
|
data?: MetricResult[];
|
||||||
|
@ -28,21 +29,6 @@ export interface GraphViewProps {
|
||||||
height?: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const promValueToNumber = (s: string): number => {
|
|
||||||
// See https://prometheus.io/docs/prometheus/latest/querying/api/#expression-query-result-formats
|
|
||||||
switch (s) {
|
|
||||||
case "NaN":
|
|
||||||
return NaN;
|
|
||||||
case "Inf":
|
|
||||||
case "+Inf":
|
|
||||||
return Infinity;
|
|
||||||
case "-Inf":
|
|
||||||
return -Infinity;
|
|
||||||
default:
|
|
||||||
return parseFloat(s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const GraphView: FC<GraphViewProps> = ({
|
const GraphView: FC<GraphViewProps> = ({
|
||||||
data = [],
|
data = [],
|
||||||
period,
|
period,
|
||||||
|
|
|
@ -40,7 +40,7 @@ const Index: FC = () => {
|
||||||
const getQueryStatsTitle = (key: keyof TopQueryStats) => {
|
const getQueryStatsTitle = (key: keyof TopQueryStats) => {
|
||||||
if (!data) return key;
|
if (!data) return key;
|
||||||
const value = data[key];
|
const value = data[key];
|
||||||
if (typeof value === "number") return formatPrettyNumber(value);
|
if (typeof value === "number") return formatPrettyNumber(value, value, value);
|
||||||
return value || key;
|
return value || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,25 @@ export const getMinFromArray = (a: number[]) => {
|
||||||
return Number.isFinite(min) ? min : null;
|
return Number.isFinite(min) ? min : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAvgFromArray = (a: number[]) => a.reduce((a,b) => a+b) / a.length;
|
export const getAvgFromArray = (a: number[]) => {
|
||||||
|
let mean = a[0];
|
||||||
|
let n = 1;
|
||||||
|
for (let i = 1; i < a.length; i++) {
|
||||||
|
const v = a[i];
|
||||||
|
if (Number.isFinite(v)) {
|
||||||
|
mean = mean * (n-1)/n + v / n;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLastFromArray = (a: number[]) => {
|
||||||
|
let len = a.length;
|
||||||
|
while (len--) {
|
||||||
|
const v = a[len];
|
||||||
|
if (Number.isFinite(v)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -13,3 +13,18 @@ export const getNameForMetric = (result: MetricBase, alias?: string, showQueryNu
|
||||||
`${e[0]}=${JSON.stringify(e[1])}`
|
`${e[0]}=${JSON.stringify(e[1])}`
|
||||||
).join(", ")}}`;
|
).join(", ")}}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const promValueToNumber = (s: string): number => {
|
||||||
|
// See https://prometheus.io/docs/prometheus/latest/querying/api/#expression-query-result-formats
|
||||||
|
switch (s) {
|
||||||
|
case "NaN":
|
||||||
|
return NaN;
|
||||||
|
case "Inf":
|
||||||
|
case "+Inf":
|
||||||
|
return Infinity;
|
||||||
|
case "-Inf":
|
||||||
|
return -Infinity;
|
||||||
|
default:
|
||||||
|
return parseFloat(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -32,10 +32,12 @@ export const formatTicks = (u: uPlot, ticks: number[], unit = ""): string[] => {
|
||||||
return ticks.map(v => `${formatPrettyNumber(v, min, max)} ${unit}`);
|
return ticks.map(v => `${formatPrettyNumber(v, min, max)} ${unit}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatPrettyNumber = (n: number | null | undefined, min = 0, max = 0): string => {
|
export const formatPrettyNumber = (n: number | null | undefined, min: number | null | undefined, max: number | null | undefined): string => {
|
||||||
if (n === undefined || n === null) {
|
if (n === undefined || n === null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
max = max || 0;
|
||||||
|
min = min || 0;
|
||||||
const range = Math.abs(max - min);
|
const range = Math.abs(max - min);
|
||||||
if (isNaN(range) || range == 0) {
|
if (isNaN(range) || range == 0) {
|
||||||
// Return the constant number as is if the range isn't set of it is too small.
|
// Return the constant number as is if the range isn't set of it is too small.
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import { MetricResult } from "../../api/types";
|
import { MetricResult } from "../../api/types";
|
||||||
import { Series } from "uplot";
|
import { Series } from "uplot";
|
||||||
import { getNameForMetric } from "../metric";
|
import { getNameForMetric, promValueToNumber } from "../metric";
|
||||||
import { BarSeriesItem, Disp, Fill, LegendItemType, Stroke } from "./types";
|
import { BarSeriesItem, Disp, Fill, LegendItemType, Stroke } from "./types";
|
||||||
import { HideSeriesArgs } from "./types";
|
import { HideSeriesArgs } from "./types";
|
||||||
import { baseContrastColors, getColorFromString } from "../color";
|
import { baseContrastColors, getColorFromString } from "../color";
|
||||||
|
import { getAvgFromArray, getMaxFromArray, getMinFromArray, getLastFromArray } from "../math";
|
||||||
|
import { formatPrettyNumber } from "./helpers";
|
||||||
|
|
||||||
interface SeriesItem extends Series {
|
export interface SeriesItem extends Series {
|
||||||
freeFormFields: {[key: string]: string};
|
freeFormFields: {[key: string]: string};
|
||||||
|
calculations: {
|
||||||
|
max: string,
|
||||||
|
avg: string,
|
||||||
|
last: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSeriesItemContext = () => {
|
export const getSeriesItemContext = () => {
|
||||||
|
@ -18,6 +25,12 @@ export const getSeriesItemContext = () => {
|
||||||
const hasBasicColors = countSavedColors < baseContrastColors.length;
|
const hasBasicColors = countSavedColors < baseContrastColors.length;
|
||||||
if (hasBasicColors) colorState[label] = colorState[label] || baseContrastColors[countSavedColors];
|
if (hasBasicColors) colorState[label] = colorState[label] || baseContrastColors[countSavedColors];
|
||||||
|
|
||||||
|
const values = d.values.map(v => promValueToNumber(v[1]));
|
||||||
|
const min = getMinFromArray(values);
|
||||||
|
const max = getMaxFromArray(values);
|
||||||
|
const avg = getAvgFromArray(values);
|
||||||
|
const last = getLastFromArray(values);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
freeFormFields: d.metric,
|
freeFormFields: d.metric,
|
||||||
|
@ -28,6 +41,11 @@ export const getSeriesItemContext = () => {
|
||||||
points: {
|
points: {
|
||||||
size: 4.2,
|
size: 4.2,
|
||||||
width: 1.4
|
width: 1.4
|
||||||
|
},
|
||||||
|
calculations: {
|
||||||
|
max: formatPrettyNumber(max, min, max),
|
||||||
|
avg: formatPrettyNumber(avg, min, max),
|
||||||
|
last: formatPrettyNumber(last, min, max),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -39,6 +57,7 @@ export const getLegendItem = (s: SeriesItem, group: number): LegendItemType => (
|
||||||
color: s.stroke as string,
|
color: s.stroke as string,
|
||||||
checked: s.show || false,
|
checked: s.show || false,
|
||||||
freeFormFields: s.freeFormFields,
|
freeFormFields: s.freeFormFields,
|
||||||
|
calculations: s.calculations,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getHideSeries = ({ hideSeries, legend, metaKey, series }: HideSeriesArgs): string[] => {
|
export const getHideSeries = ({ hideSeries, legend, metaKey, series }: HideSeriesArgs): string[] => {
|
||||||
|
|
|
@ -21,6 +21,11 @@ export interface LegendItemType {
|
||||||
color: string;
|
color: string;
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
freeFormFields: {[key: string]: string};
|
freeFormFields: {[key: string]: string};
|
||||||
|
calculations: {
|
||||||
|
max: string;
|
||||||
|
avg: string;
|
||||||
|
last: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BarSeriesItem {
|
export interface BarSeriesItem {
|
||||||
|
|
|
@ -19,6 +19,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
This also means that `-remoteRead.ignoreRestoreErrors` command-line flag becomes deprecated now and will have no effect if configured.
|
This also means that `-remoteRead.ignoreRestoreErrors` command-line flag becomes deprecated now and will have no effect if configured.
|
||||||
While previously state restore attempt was made for all the loaded alerting rules, now it is called only for alerts which became active after the first evaluation. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2608).
|
While previously state restore attempt was made for all the loaded alerting rules, now it is called only for alerts which became active after the first evaluation. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2608).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): optimize VMUI for use from smarthones and tablets. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3707).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): optimize VMUI for use from smarthones and tablets. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3707).
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add avg/max/last values to line legends and tooltips for graphs. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3706).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): hide the default `per-job resource usage` dashboard if there is a custom dashboard exists at the directory specified via `-vmui.customDashboardsPath` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3740).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): hide the default `per-job resource usage` dashboard if there is a custom dashboard exists at the directory specified via `-vmui.customDashboardsPath` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3740).
|
||||||
|
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): fix panic in [HashiCorp Nomad service discovery](https://docs.victoriametrics.com/sd_configs.html#nomad_sd_configs). Thanks to @mr-karan for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3784).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): fix panic in [HashiCorp Nomad service discovery](https://docs.victoriametrics.com/sd_configs.html#nomad_sd_configs). Thanks to @mr-karan for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3784).
|
||||||
|
|
Loading…
Reference in a new issue