mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add ability hide query (#3359)
* feat: add ability hide query * fix: change logic hide query * fix: remove console.log
This commit is contained in:
parent
478a295208
commit
734172d2d0
9 changed files with 122 additions and 28 deletions
|
@ -18,7 +18,7 @@ export interface QueryEditorProps {
|
||||||
error?: ErrorTypes | string;
|
error?: ErrorTypes | string;
|
||||||
options: string[];
|
options: string[];
|
||||||
label: string;
|
label: string;
|
||||||
size?: "small" | "medium" | undefined;
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueryEditor: FC<QueryEditorProps> = ({
|
const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
|
@ -31,6 +31,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
error,
|
error,
|
||||||
options,
|
options,
|
||||||
label,
|
label,
|
||||||
|
disabled = false
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const [focusOption, setFocusOption] = useState(-1);
|
const [focusOption, setFocusOption] = useState(-1);
|
||||||
|
@ -86,6 +87,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
|
|
||||||
// Enter
|
// Enter
|
||||||
if (enter && hasAutocomplete && !shiftKey && !ctrlMetaKey) {
|
if (enter && hasAutocomplete && !shiftKey && !ctrlMetaKey) {
|
||||||
|
if (disabled) return;
|
||||||
onChange(foundOptions[focusOption]);
|
onChange(foundOptions[focusOption]);
|
||||||
setOpenAutocomplete(false);
|
setOpenAutocomplete(false);
|
||||||
} else if (enter && !shiftKey) {
|
} else if (enter && !shiftKey) {
|
||||||
|
@ -98,6 +100,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const createHandlerOnChangeAutocomplete = (item: string) => () => {
|
const createHandlerOnChangeAutocomplete = (item: string) => () => {
|
||||||
|
if (disabled) return;
|
||||||
onChange(item);
|
onChange(item);
|
||||||
handleCloseAutocomplete();
|
handleCloseAutocomplete();
|
||||||
};
|
};
|
||||||
|
@ -127,6 +130,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
error={error}
|
error={error}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<Popper
|
<Popper
|
||||||
open={openAutocomplete}
|
open={openAutocomplete}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import "./style.scss";
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
variant?: "contained" | "outlined" | "text"
|
variant?: "contained" | "outlined" | "text"
|
||||||
color?: "primary" | "secondary" | "success" | "error"
|
color?: "primary" | "secondary" | "success" | "error" | "gray"
|
||||||
size?: "small" | "medium" | "large"
|
size?: "small" | "medium" | "large"
|
||||||
endIcon?: ReactNode
|
endIcon?: ReactNode
|
||||||
startIcon?: ReactNode
|
startIcon?: ReactNode
|
||||||
|
|
|
@ -135,6 +135,14 @@ $button-radius: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_contained_gray {
|
||||||
|
color: $color-text-secondary;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background-color: $color-text-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* variant TEXT */
|
/* variant TEXT */
|
||||||
&_text_primary {
|
&_text_primary {
|
||||||
|
@ -153,6 +161,10 @@ $button-radius: 6px;
|
||||||
color: $color-error;
|
color: $color-error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_text_gray {
|
||||||
|
color: $color-text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* variant OUTLINED */
|
/* variant OUTLINED */
|
||||||
&_outlined_primary {
|
&_outlined_primary {
|
||||||
|
@ -174,4 +186,9 @@ $button-radius: 6px;
|
||||||
border: 1px solid $color-success;
|
border: 1px solid $color-success;
|
||||||
color: $color-success;
|
color: $color-success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_outlined_gray {
|
||||||
|
border: 1px solid $color-text-secondary;
|
||||||
|
color: $color-text-secondary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,3 +267,26 @@ export const DoneIcon = () => (
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const VisibilityIcon = () => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export const VisibilityOffIcon = () => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78 3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
|
@ -17,9 +17,10 @@ interface FetchQueryParams {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
display?: DisplayType,
|
display?: DisplayType,
|
||||||
customStep: number,
|
customStep: number,
|
||||||
|
hideQuery?: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFetchQuery = ({ predefinedQuery, visible, display, customStep }: FetchQueryParams): {
|
export const useFetchQuery = ({ predefinedQuery, visible, display, customStep, hideQuery = [] }: FetchQueryParams): {
|
||||||
fetchUrl?: string[],
|
fetchUrl?: string[],
|
||||||
isLoading: boolean,
|
isLoading: boolean,
|
||||||
graphData?: MetricResult[],
|
graphData?: MetricResult[],
|
||||||
|
@ -111,14 +112,14 @@ export const useFetchQuery = ({ predefinedQuery, visible, display, customStep }:
|
||||||
} else if (isValidHttpUrl(serverUrl)) {
|
} else if (isValidHttpUrl(serverUrl)) {
|
||||||
const updatedPeriod = { ...period };
|
const updatedPeriod = { ...period };
|
||||||
updatedPeriod.step = customStep;
|
updatedPeriod.step = customStep;
|
||||||
return expr.filter(q => q.trim()).map(q => displayChart
|
return expr.filter((q, i) => q.trim() && !hideQuery.includes(i)).map(q => displayChart
|
||||||
? getQueryRangeUrl(serverUrl, q, updatedPeriod, nocache, isTracingEnabled)
|
? getQueryRangeUrl(serverUrl, q, updatedPeriod, nocache, isTracingEnabled)
|
||||||
: getQueryUrl(serverUrl, q, updatedPeriod, isTracingEnabled));
|
: getQueryUrl(serverUrl, q, updatedPeriod, isTracingEnabled));
|
||||||
} else {
|
} else {
|
||||||
setError(ErrorTypes.validServer);
|
setError(ErrorTypes.validServer);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[serverUrl, period, displayType, customStep]);
|
[serverUrl, period, displayType, customStep, hideQuery]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible || !fetchUrl?.length) return;
|
if (!visible || !fetchUrl?.length) return;
|
||||||
|
|
|
@ -6,24 +6,28 @@ import usePrevious from "../../../hooks/usePrevious";
|
||||||
import { MAX_QUERY_FIELDS } from "../../../constants/graph";
|
import { MAX_QUERY_FIELDS } from "../../../constants/graph";
|
||||||
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
|
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
|
||||||
import { useTimeDispatch } from "../../../state/time/TimeStateContext";
|
import { useTimeDispatch } from "../../../state/time/TimeStateContext";
|
||||||
import { DeleteIcon, PlayIcon, PlusIcon } from "../../../components/Main/Icons";
|
import { DeleteIcon, PlayIcon, PlusIcon, VisibilityIcon, VisibilityOffIcon } from "../../../components/Main/Icons";
|
||||||
import Button from "../../../components/Main/Button/Button";
|
import Button from "../../../components/Main/Button/Button";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
|
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
export interface QueryConfiguratorProps {
|
export interface QueryConfiguratorProps {
|
||||||
error?: ErrorTypes | string;
|
error?: ErrorTypes | string;
|
||||||
queryOptions: string[]
|
queryOptions: string[]
|
||||||
|
onHideQuery: (queries: number[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions }) => {
|
const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions, onHideQuery }) => {
|
||||||
|
|
||||||
const { query, queryHistory, autocomplete } = useQueryState();
|
const { query, queryHistory, autocomplete } = useQueryState();
|
||||||
const queryDispatch = useQueryDispatch();
|
const queryDispatch = useQueryDispatch();
|
||||||
const timeDispatch = useTimeDispatch();
|
const timeDispatch = useTimeDispatch();
|
||||||
|
|
||||||
const [stateQuery, setStateQuery] = useState(query || []);
|
const [stateQuery, setStateQuery] = useState(query || []);
|
||||||
|
const [hideQuery, setHideQuery] = useState<number[]>([]);
|
||||||
const prevStateQuery = usePrevious(stateQuery) as (undefined | string[]);
|
const prevStateQuery = usePrevious(stateQuery) as (undefined | string[]);
|
||||||
|
|
||||||
const updateHistory = () => {
|
const updateHistory = () => {
|
||||||
queryDispatch({
|
queryDispatch({
|
||||||
type: "SET_QUERY_HISTORY", payload: stateQuery.map((q, i) => {
|
type: "SET_QUERY_HISTORY", payload: stateQuery.map((q, i) => {
|
||||||
|
@ -51,6 +55,10 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions })
|
||||||
setStateQuery(prev => prev.filter((q, i) => i !== index));
|
setStateQuery(prev => prev.filter((q, i) => i !== index));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onToggleHideQuery = (index: number) => {
|
||||||
|
setHideQuery(prev => prev.includes(index) ? prev.filter(n => n !== index) : [...prev, index]);
|
||||||
|
};
|
||||||
|
|
||||||
const handleChangeQuery = (value: string, index: number) => {
|
const handleChangeQuery = (value: string, index: number) => {
|
||||||
setStateQuery(prev => prev.map((q, i) => i === index ? value : q));
|
setStateQuery(prev => prev.map((q, i) => i === index ? value : q));
|
||||||
};
|
};
|
||||||
|
@ -76,6 +84,11 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions })
|
||||||
|
|
||||||
const createHandlerRemoveQuery = (i: number) => () => {
|
const createHandlerRemoveQuery = (i: number) => () => {
|
||||||
onRemoveQuery(i);
|
onRemoveQuery(i);
|
||||||
|
setHideQuery(prev => prev.map(n => n > i ? n - 1: n));
|
||||||
|
};
|
||||||
|
|
||||||
|
const createHandlerHideQuery = (i: number) => () => {
|
||||||
|
onToggleHideQuery(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -84,11 +97,18 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions })
|
||||||
}
|
}
|
||||||
}, [stateQuery]);
|
}, [stateQuery]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onHideQuery(hideQuery);
|
||||||
|
}, [hideQuery]);
|
||||||
|
|
||||||
return <div className="vm-query-configurator vm-block">
|
return <div className="vm-query-configurator vm-block">
|
||||||
<div className="vm-query-configurator-list">
|
<div className="vm-query-configurator-list">
|
||||||
{stateQuery.map((q, i) => (
|
{stateQuery.map((q, i) => (
|
||||||
<div
|
<div
|
||||||
className="vm-query-configurator-list-row"
|
className={classNames({
|
||||||
|
"vm-query-configurator-list-row": true,
|
||||||
|
"vm-query-configurator-list-row_disabled": hideQuery.includes(i)
|
||||||
|
})}
|
||||||
key={i}
|
key={i}
|
||||||
>
|
>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
|
@ -101,8 +121,18 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({ error, queryOptions })
|
||||||
onEnter={onRunQuery}
|
onEnter={onRunQuery}
|
||||||
onChange={createHandlerChangeQuery(i)}
|
onChange={createHandlerChangeQuery(i)}
|
||||||
label={`Query ${i + 1}`}
|
label={`Query ${i + 1}`}
|
||||||
size={"small"}
|
disabled={hideQuery.includes(i)}
|
||||||
/>
|
/>
|
||||||
|
<Tooltip title={hideQuery.includes(i) ? "Enable query" : "Disable query"}>
|
||||||
|
<div className="vm-query-configurator-list-row__button">
|
||||||
|
<Button
|
||||||
|
variant={"text"}
|
||||||
|
color={"gray"}
|
||||||
|
startIcon={hideQuery.includes(i) ? <VisibilityOffIcon/> : <VisibilityIcon/>}
|
||||||
|
onClick={createHandlerHideQuery(i)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
{stateQuery.length > 1 && (
|
{stateQuery.length > 1 && (
|
||||||
<Tooltip title="Remove Query">
|
<Tooltip title="Remove Query">
|
||||||
<div className="vm-query-configurator-list-row__button">
|
<div className="vm-query-configurator-list-row__button">
|
||||||
|
|
|
@ -9,10 +9,15 @@
|
||||||
|
|
||||||
&-row {
|
&-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $padding-small;
|
gap: $padding-small;
|
||||||
|
|
||||||
|
&_disabled {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
|
|
|
@ -29,10 +29,18 @@ const CustomPanel: FC = () => {
|
||||||
|
|
||||||
const [displayColumns, setDisplayColumns] = useState<string[]>();
|
const [displayColumns, setDisplayColumns] = useState<string[]>();
|
||||||
const [tracesState, setTracesState] = useState<Trace[]>([]);
|
const [tracesState, setTracesState] = useState<Trace[]>([]);
|
||||||
|
const [hideQuery, setHideQuery] = useState<number[]>([]);
|
||||||
|
|
||||||
const { customStep, yaxis } = useGraphState();
|
const { customStep, yaxis } = useGraphState();
|
||||||
const graphDispatch = useGraphDispatch();
|
const graphDispatch = useGraphDispatch();
|
||||||
|
|
||||||
|
const { queryOptions } = useFetchQueryOptions();
|
||||||
|
const { isLoading, liveData, graphData, error, warning, traces } = useFetchQuery({
|
||||||
|
visible: true,
|
||||||
|
customStep,
|
||||||
|
hideQuery
|
||||||
|
});
|
||||||
|
|
||||||
const setYaxisLimits = (limits: AxisRange) => {
|
const setYaxisLimits = (limits: AxisRange) => {
|
||||||
graphDispatch({ type: "SET_YAXIS_LIMITS", payload: limits });
|
graphDispatch({ type: "SET_YAXIS_LIMITS", payload: limits });
|
||||||
};
|
};
|
||||||
|
@ -45,17 +53,15 @@ const CustomPanel: FC = () => {
|
||||||
timeDispatch({ type: "SET_PERIOD", payload: { from, to } });
|
timeDispatch({ type: "SET_PERIOD", payload: { from, to } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { queryOptions } = useFetchQueryOptions();
|
|
||||||
const { isLoading, liveData, graphData, error, warning, traces } = useFetchQuery({
|
|
||||||
visible: true,
|
|
||||||
customStep
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleTraceDelete = (trace: Trace) => {
|
const handleTraceDelete = (trace: Trace) => {
|
||||||
const updatedTraces = tracesState.filter((data) => data.idValue !== trace.idValue);
|
const updatedTraces = tracesState.filter((data) => data.idValue !== trace.idValue);
|
||||||
setTracesState([...updatedTraces]);
|
setTracesState([...updatedTraces]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleHideQuery = (queries: number[]) => {
|
||||||
|
setHideQuery(queries);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (traces) {
|
if (traces) {
|
||||||
setTracesState([...tracesState, ...traces]);
|
setTracesState([...tracesState, ...traces]);
|
||||||
|
@ -71,6 +77,7 @@ const CustomPanel: FC = () => {
|
||||||
<QueryConfigurator
|
<QueryConfigurator
|
||||||
error={error}
|
error={error}
|
||||||
queryOptions={queryOptions}
|
queryOptions={queryOptions}
|
||||||
|
onHideQuery={handleHideQuery}
|
||||||
/>
|
/>
|
||||||
{isTracingEnabled && (
|
{isTracingEnabled && (
|
||||||
<div className="vm-custom-panel__trace">
|
<div className="vm-custom-panel__trace">
|
||||||
|
@ -80,22 +87,26 @@ const CustomPanel: FC = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isLoading && <Spinner />}
|
||||||
{error && <Alert variant="error">{error}</Alert>}
|
{error && <Alert variant="error">{error}</Alert>}
|
||||||
{warning && <Alert variant="warning">{warning}</Alert>}
|
{warning && <Alert variant="warning">{warning}</Alert>}
|
||||||
<div className="vm-custom-panel-body vm-block">
|
<div className="vm-custom-panel-body vm-block">
|
||||||
{isLoading && <Spinner />}
|
|
||||||
<div className="vm-custom-panel-body-header">
|
<div className="vm-custom-panel-body-header">
|
||||||
<DisplayTypeSwitch/>
|
<DisplayTypeSwitch/>
|
||||||
{displayType === "chart" && <GraphSettings
|
{displayType === "chart" && (
|
||||||
|
<GraphSettings
|
||||||
yaxis={yaxis}
|
yaxis={yaxis}
|
||||||
setYaxisLimits={setYaxisLimits}
|
setYaxisLimits={setYaxisLimits}
|
||||||
toggleEnableLimits={toggleEnableLimits}
|
toggleEnableLimits={toggleEnableLimits}
|
||||||
/>}
|
/>
|
||||||
{displayType === "table" && <TableSettings
|
)}
|
||||||
|
{displayType === "table" && (
|
||||||
|
<TableSettings
|
||||||
data={liveData || []}
|
data={liveData || []}
|
||||||
defaultColumns={displayColumns}
|
defaultColumns={displayColumns}
|
||||||
onChange={setDisplayColumns}
|
onChange={setDisplayColumns}
|
||||||
/>}
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{graphData && period && (displayType === "chart") && (
|
{graphData && period && (displayType === "chart") && (
|
||||||
<GraphView
|
<GraphView
|
||||||
|
@ -108,7 +119,9 @@ const CustomPanel: FC = () => {
|
||||||
setPeriod={setPeriod}
|
setPeriod={setPeriod}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{liveData && (displayType === "code") && <JsonView data={liveData}/>}
|
{liveData && (displayType === "code") && (
|
||||||
|
<JsonView data={liveData}/>
|
||||||
|
)}
|
||||||
{liveData && (displayType === "table") && (
|
{liveData && (displayType === "table") && (
|
||||||
<TableView
|
<TableView
|
||||||
data={liveData}
|
data={liveData}
|
||||||
|
|
|
@ -18,6 +18,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add [range_linear_regression](https://docs.victoriametrics.com/MetricsQL.html#range_linear_regression) function for calculating [simple linear regression](https://en.wikipedia.org/wiki/Simple_linear_regression) over the input time series on the selected time range. This function is useful for predictions and capacity planning. For example, `range_linear_regression(process_resident_memory_bytes)` can predict future memory usage based on the past memory usage.
|
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add [range_linear_regression](https://docs.victoriametrics.com/MetricsQL.html#range_linear_regression) function for calculating [simple linear regression](https://en.wikipedia.org/wiki/Simple_linear_regression) over the input time series on the selected time range. This function is useful for predictions and capacity planning. For example, `range_linear_regression(process_resident_memory_bytes)` can predict future memory usage based on the past memory usage.
|
||||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add [range_stddev](https://docs.victoriametrics.com/MetricsQL.html#range_stddev) and [range_stdvar](https://docs.victoriametrics.com/MetricsQL.html#range_stdvar) functions.
|
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add [range_stddev](https://docs.victoriametrics.com/MetricsQL.html#range_stddev) and [range_stdvar](https://docs.victoriametrics.com/MetricsQL.html#range_stdvar) functions.
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): improve structure project, change state management, reduce bundle size, remove Material-UI. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3298)
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): improve structure project, change state management, reduce bundle size, remove Material-UI. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3298)
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to hide the query.
|
||||||
|
|
||||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): save the value of the switches "Trace request" and "Disable cache" after page reload.
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): save the value of the switches "Trace request" and "Disable cache" after page reload.
|
||||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): properly show the tab when navigating from the Prometheus URL in Grafana.
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): properly show the tab when navigating from the Prometheus URL in Grafana.
|
||||||
|
|
Loading…
Reference in a new issue