mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add notification for non-matching queries (#4301)
vmui: add notification for non-matching queries (#4211) https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4211
This commit is contained in:
parent
ddc3f0c5c5
commit
ff39df74d3
7 changed files with 64 additions and 3 deletions
|
@ -20,3 +20,8 @@ export interface TracingData {
|
||||||
duration_msec: number;
|
duration_msec: number;
|
||||||
children: TracingData[];
|
children: TracingData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QueryStats {
|
||||||
|
seriesFetched?: string;
|
||||||
|
resultLength?: number;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { ErrorTypes } from "../../../types";
|
||||||
import TextField from "../../Main/TextField/TextField";
|
import TextField from "../../Main/TextField/TextField";
|
||||||
import Autocomplete from "../../Main/Autocomplete/Autocomplete";
|
import Autocomplete from "../../Main/Autocomplete/Autocomplete";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
|
import { QueryStats } from "../../../api/types";
|
||||||
|
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||||
|
import { WarningIcon } from "../../Main/Icons";
|
||||||
|
|
||||||
export interface QueryEditorProps {
|
export interface QueryEditorProps {
|
||||||
onChange: (query: string) => void;
|
onChange: (query: string) => void;
|
||||||
|
@ -14,6 +17,7 @@ export interface QueryEditorProps {
|
||||||
oneLiner?: boolean;
|
oneLiner?: boolean;
|
||||||
autocomplete: boolean;
|
autocomplete: boolean;
|
||||||
error?: ErrorTypes | string;
|
error?: ErrorTypes | string;
|
||||||
|
stats?: QueryStats;
|
||||||
options: string[];
|
options: string[];
|
||||||
label: string;
|
label: string;
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
@ -27,6 +31,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
onArrowDown,
|
onArrowDown,
|
||||||
autocomplete,
|
autocomplete,
|
||||||
error,
|
error,
|
||||||
|
stats,
|
||||||
options,
|
options,
|
||||||
label,
|
label,
|
||||||
disabled = false
|
disabled = false
|
||||||
|
@ -34,6 +39,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
|
|
||||||
const [openAutocomplete, setOpenAutocomplete] = useState(false);
|
const [openAutocomplete, setOpenAutocomplete] = useState(false);
|
||||||
const autocompleteAnchorEl = useRef<HTMLDivElement>(null);
|
const autocompleteAnchorEl = useRef<HTMLDivElement>(null);
|
||||||
|
const showSeriesFetchedWarning = stats?.seriesFetched === "0" && !stats.resultLength;
|
||||||
|
|
||||||
const handleSelect = (val: string) => {
|
const handleSelect = (val: string) => {
|
||||||
onChange(val);
|
onChange(val);
|
||||||
|
@ -90,6 +96,23 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
onOpenAutocomplete={setOpenAutocomplete}
|
onOpenAutocomplete={setOpenAutocomplete}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{showSeriesFetchedWarning && (
|
||||||
|
<div className="vm-query-editor-warning">
|
||||||
|
<Tooltip
|
||||||
|
placement="bottom-right"
|
||||||
|
title={(
|
||||||
|
<span className="vm-query-editor-warning__tooltip">
|
||||||
|
{`No match!
|
||||||
|
This query hasn't selected any time series from database.
|
||||||
|
Either the requested metrics are missing in the database,
|
||||||
|
or there is a typo in series selector.`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<WarningIcon/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
@use "src/styles/variables" as *;
|
@use "src/styles/variables" as *;
|
||||||
|
|
||||||
.vm-query-editor {
|
.vm-query-editor {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&-autocomplete {
|
&-autocomplete {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-warning {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: $padding-global;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
color: $color-warning;
|
||||||
|
|
||||||
|
&__tooltip {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useEffect, useMemo, useState } from "preact/compat";
|
import { useCallback, useEffect, useMemo, useState } from "preact/compat";
|
||||||
import { getQueryRangeUrl, getQueryUrl } from "../api/query-range";
|
import { getQueryRangeUrl, getQueryUrl } from "../api/query-range";
|
||||||
import { useAppState } from "../state/common/StateContext";
|
import { useAppState } from "../state/common/StateContext";
|
||||||
import { InstantMetricResult, MetricBase, MetricResult } from "../api/types";
|
import { InstantMetricResult, MetricBase, MetricResult, QueryStats } from "../api/types";
|
||||||
import { isValidHttpUrl } from "../utils/url";
|
import { isValidHttpUrl } from "../utils/url";
|
||||||
import { ErrorTypes, SeriesLimits } from "../types";
|
import { ErrorTypes, SeriesLimits } from "../types";
|
||||||
import debounce from "lodash.debounce";
|
import debounce from "lodash.debounce";
|
||||||
|
@ -28,9 +28,10 @@ interface FetchQueryReturn {
|
||||||
liveData?: InstantMetricResult[],
|
liveData?: InstantMetricResult[],
|
||||||
error?: ErrorTypes | string,
|
error?: ErrorTypes | string,
|
||||||
queryErrors: (ErrorTypes | string)[],
|
queryErrors: (ErrorTypes | string)[],
|
||||||
|
queryStats: QueryStats[],
|
||||||
warning?: string,
|
warning?: string,
|
||||||
traces?: Trace[],
|
traces?: Trace[],
|
||||||
isHistogram: boolean,
|
isHistogram: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FetchDataParams {
|
interface FetchDataParams {
|
||||||
|
@ -62,6 +63,7 @@ export const useFetchQuery = ({
|
||||||
const [traces, setTraces] = useState<Trace[]>();
|
const [traces, setTraces] = useState<Trace[]>();
|
||||||
const [error, setError] = useState<ErrorTypes | string>();
|
const [error, setError] = useState<ErrorTypes | string>();
|
||||||
const [queryErrors, setQueryErrors] = useState<(ErrorTypes | string)[]>([]);
|
const [queryErrors, setQueryErrors] = useState<(ErrorTypes | string)[]>([]);
|
||||||
|
const [queryStats, setQueryStats] = useState<QueryStats[]>([]);
|
||||||
const [warning, setWarning] = useState<string>();
|
const [warning, setWarning] = useState<string>();
|
||||||
const [fetchQueue, setFetchQueue] = useState<AbortController[]>([]);
|
const [fetchQueue, setFetchQueue] = useState<AbortController[]>([]);
|
||||||
const [isHistogram, setIsHistogram] = useState(false);
|
const [isHistogram, setIsHistogram] = useState(false);
|
||||||
|
@ -90,6 +92,7 @@ export const useFetchQuery = ({
|
||||||
const isHideQuery = hideQuery?.includes(counter - 1);
|
const isHideQuery = hideQuery?.includes(counter - 1);
|
||||||
if (isHideQuery) {
|
if (isHideQuery) {
|
||||||
setQueryErrors(prev => [...prev, ""]);
|
setQueryErrors(prev => [...prev, ""]);
|
||||||
|
setQueryStats(prev => [...prev, {}]);
|
||||||
counter++;
|
counter++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +101,10 @@ export const useFetchQuery = ({
|
||||||
const resp = await response.json();
|
const resp = await response.json();
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
setQueryStats(prev => [...prev, {
|
||||||
|
...resp?.stats,
|
||||||
|
resultLength: resp.data.result.length,
|
||||||
|
}]);
|
||||||
setQueryErrors(prev => [...prev, ""]);
|
setQueryErrors(prev => [...prev, ""]);
|
||||||
|
|
||||||
if (resp.trace) {
|
if (resp.trace) {
|
||||||
|
@ -139,6 +146,7 @@ export const useFetchQuery = ({
|
||||||
const fetchUrl = useMemo(() => {
|
const fetchUrl = useMemo(() => {
|
||||||
setError("");
|
setError("");
|
||||||
setQueryErrors([]);
|
setQueryErrors([]);
|
||||||
|
setQueryStats([]);
|
||||||
const expr = predefinedQuery ?? query;
|
const expr = predefinedQuery ?? query;
|
||||||
const displayChart = (display || displayType) === "chart";
|
const displayChart = (display || displayType) === "chart";
|
||||||
if (!period) return;
|
if (!period) return;
|
||||||
|
@ -184,5 +192,5 @@ export const useFetchQuery = ({
|
||||||
setFetchQueue(fetchQueue.filter(f => !f.signal.aborted));
|
setFetchQueue(fetchQueue.filter(f => !f.signal.aborted));
|
||||||
}, [fetchQueue]);
|
}, [fetchQueue]);
|
||||||
|
|
||||||
return { fetchUrl, isLoading, graphData, liveData, error, queryErrors, warning, traces, isHistogram };
|
return { fetchUrl, isLoading, graphData, liveData, error, queryErrors, queryStats, warning, traces, isHistogram };
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,9 +14,11 @@ import classNames from "classnames";
|
||||||
import { MouseEvent as ReactMouseEvent } from "react";
|
import { MouseEvent as ReactMouseEvent } from "react";
|
||||||
import { arrayEquals } from "../../../utils/array";
|
import { arrayEquals } from "../../../utils/array";
|
||||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||||
|
import { QueryStats } from "../../../api/types";
|
||||||
|
|
||||||
export interface QueryConfiguratorProps {
|
export interface QueryConfiguratorProps {
|
||||||
errors: (ErrorTypes | string)[];
|
errors: (ErrorTypes | string)[];
|
||||||
|
stats: QueryStats[];
|
||||||
queryOptions: string[]
|
queryOptions: string[]
|
||||||
onHideQuery: (queries: number[]) => void
|
onHideQuery: (queries: number[]) => void
|
||||||
onRunQuery: () => void
|
onRunQuery: () => void
|
||||||
|
@ -24,6 +26,7 @@ export interface QueryConfiguratorProps {
|
||||||
|
|
||||||
const QueryConfigurator: FC<QueryConfiguratorProps> = ({
|
const QueryConfigurator: FC<QueryConfiguratorProps> = ({
|
||||||
errors,
|
errors,
|
||||||
|
stats,
|
||||||
queryOptions,
|
queryOptions,
|
||||||
onHideQuery,
|
onHideQuery,
|
||||||
onRunQuery
|
onRunQuery
|
||||||
|
@ -142,6 +145,7 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
|
||||||
autocomplete={autocomplete}
|
autocomplete={autocomplete}
|
||||||
options={queryOptions}
|
options={queryOptions}
|
||||||
error={errors[i]}
|
error={errors[i]}
|
||||||
|
stats={stats[i]}
|
||||||
onArrowUp={createHandlerArrow(-1, i)}
|
onArrowUp={createHandlerArrow(-1, i)}
|
||||||
onArrowDown={createHandlerArrow(1, i)}
|
onArrowDown={createHandlerArrow(1, i)}
|
||||||
onEnter={handleRunQuery}
|
onEnter={handleRunQuery}
|
||||||
|
|
|
@ -49,6 +49,7 @@ const CustomPanel: FC = () => {
|
||||||
graphData,
|
graphData,
|
||||||
error,
|
error,
|
||||||
queryErrors,
|
queryErrors,
|
||||||
|
queryStats,
|
||||||
warning,
|
warning,
|
||||||
traces,
|
traces,
|
||||||
isHistogram
|
isHistogram
|
||||||
|
@ -115,6 +116,7 @@ const CustomPanel: FC = () => {
|
||||||
>
|
>
|
||||||
<QueryConfigurator
|
<QueryConfigurator
|
||||||
errors={!hideError ? queryErrors : []}
|
errors={!hideError ? queryErrors : []}
|
||||||
|
stats={queryStats}
|
||||||
queryOptions={queryOptions}
|
queryOptions={queryOptions}
|
||||||
onHideQuery={handleHideQuery}
|
onHideQuery={handleHideQuery}
|
||||||
onRunQuery={handleRunQuery}
|
onRunQuery={handleRunQuery}
|
||||||
|
|
|
@ -48,6 +48,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add an ability to copy and execute queries listed at [top queries](https://docs.victoriametrics.com/#top-queries) page. Also make more human readable the query duration column. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4292) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4299).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add an ability to copy and execute queries listed at [top queries](https://docs.victoriametrics.com/#top-queries) page. Also make more human readable the query duration column. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4292) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4299).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): increase default font size for better readability.
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): increase default font size for better readability.
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): [cardinality explorer](https://docs.victoriametrics.com/#cardinality-explorer): return back a table with labels containing the highest number of unique label values. See [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4213).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): [cardinality explorer](https://docs.victoriametrics.com/#cardinality-explorer): return back a table with labels containing the highest number of unique label values. See [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4213).
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add notification icon for queries that do not match any time series. A warning icon appears next to the query field when the executed query does not match any time series. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4211).
|
||||||
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add `-s3StorageClass` command-line flag for setting the storage class for AWS S3 backups. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4164). Thanks to @justcompile for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4166).
|
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add `-s3StorageClass` command-line flag for setting the storage class for AWS S3 backups. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4164). Thanks to @justcompile for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4166).
|
||||||
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): store backup creation and completion time in `backup_complete.ignore` file of backup contents. This allows determining the exact timestamp when the backup was created and completed.
|
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): store backup creation and completion time in `backup_complete.ignore` file of backup contents. This allows determining the exact timestamp when the backup was created and completed.
|
||||||
* FEATURE: [vmbackupmanager](https://docs.victoriametrics.com/vmbackupmanager.html): add `created_at` field to the output of `/api/v1/backups` API and `vmbackupmanager backup list` command. See this [doc](https://docs.victoriametrics.com/vmbackupmanager.html#api-methods) for data format details.
|
* FEATURE: [vmbackupmanager](https://docs.victoriametrics.com/vmbackupmanager.html): add `created_at` field to the output of `/api/v1/backups` API and `vmbackupmanager backup list` command. See this [doc](https://docs.victoriametrics.com/vmbackupmanager.html#api-methods) for data format details.
|
||||||
|
|
Loading…
Reference in a new issue