diff --git a/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx b/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx index 0340a4ffd5..5b0deb156c 100644 --- a/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx @@ -553,3 +553,20 @@ export const SearchIcon = () => ( > ); + +export const SpinnerIcon = () => ( + + + + + +); diff --git a/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx b/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx new file mode 100644 index 0000000000..97bb5efa81 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx @@ -0,0 +1,13 @@ +import React, { FC } from "preact/compat"; +import "./style.scss"; + +const LineLoader: FC = () => { + return ( +
+
+
+
+ ); +}; + +export default LineLoader; diff --git a/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss b/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss new file mode 100644 index 0000000000..2c17a351d2 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss @@ -0,0 +1,39 @@ +@use "src/styles/variables" as *; + +.vm-line-loader { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + z-index: 2; + overflow: hidden; + + &__background { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: $color-text; + opacity: 0.1; + } + + &__line { + position: absolute; + width: 10%; + height: 100%; + background-color: $color-primary; + animation: slide 2s infinite linear; + opacity: 0.8; + } +} + +@keyframes slide { + 0% { + left: 0; + } + 100% { + left: 100%; + } +} diff --git a/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts b/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts index 7aba2dc0a8..f23d5ba032 100644 --- a/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts +++ b/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts @@ -34,7 +34,8 @@ interface FetchQueryReturn { queryStats: QueryStats[], warning?: string, traces?: Trace[], - isHistogram: boolean + isHistogram: boolean, + abortFetch: () => void } interface FetchDataParams { @@ -160,6 +161,7 @@ export const useFetchQuery = ({ const error = e as Error; if (error.name === "AbortError") { // Aborts are expected, don't show an error for them. + setIsLoading(false); return; } const helperText = "Please check your serverURL settings and confirm server availability."; @@ -197,6 +199,13 @@ export const useFetchQuery = ({ }, [serverUrl, period, displayType, customStep, hideQuery]); + const abortFetch = useCallback(() => { + fetchQueue.map(f => f.abort()); + setFetchQueue([]); + setGraphData([]); + setLiveData([]); + }, [fetchQueue]); + const [prevUrl, setPrevUrl] = useState([]); useEffect(() => { @@ -238,6 +247,7 @@ export const useFetchQuery = ({ queryStats, warning, traces, - isHistogram + isHistogram, + abortFetch, }; }; diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx index 1a8c8e8ad0..c321aff10f 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx @@ -10,6 +10,7 @@ import { PlayIcon, PlusIcon, Prettify, + SpinnerIcon, VisibilityIcon, VisibilityOffIcon } from "../../../components/Main/Icons"; @@ -30,8 +31,10 @@ export interface QueryConfiguratorProps { setQueryErrors: Dispatch>; setHideError: Dispatch>; stats: QueryStats[]; + isLoading?: boolean; onHideQuery?: (queries: number[]) => void onRunQuery: () => void; + abortFetch?: () => void; hideButtons?: { addQuery?: boolean; prettify?: boolean; @@ -46,8 +49,10 @@ const QueryConfigurator: FC = ({ setQueryErrors, setHideError, stats, + isLoading, onHideQuery, onRunQuery, + abortFetch, hideButtons }) => { @@ -84,6 +89,10 @@ const QueryConfigurator: FC = ({ }; const handleRunQuery = () => { + if (isLoading) { + abortFetch && abortFetch(); + return; + } updateHistory(); queryDispatch({ type: "SET_QUERY", payload: stateQuery }); timeDispatch({ type: "RUN_QUERY" }); @@ -271,9 +280,9 @@ const QueryConfigurator: FC = ({ diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx index cf56a8c407..0f04ab0337 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx @@ -3,7 +3,7 @@ import QueryConfigurator from "./QueryConfigurator/QueryConfigurator"; import { useFetchQuery } from "../../hooks/useFetchQuery"; import { DisplayTypeSwitch } from "./DisplayTypeSwitch"; import { useGraphDispatch, useGraphState } from "../../state/graph/GraphStateContext"; -import Spinner from "../../components/Main/Spinner/Spinner"; +import LineLoader from "../../components/Main/LineLoader/LineLoader"; import { useCustomPanelState } from "../../state/customPanel/CustomPanelStateContext"; import { useQueryState } from "../../state/query/QueryStateContext"; import { useSetQueryParams } from "./hooks/useSetQueryParams"; @@ -45,7 +45,8 @@ const CustomPanel: FC = () => { queryStats, warning, traces, - isHistogram + isHistogram, + abortFetch, } = useFetchQuery({ visible: true, customStep, @@ -80,14 +81,15 @@ const CustomPanel: FC = () => { setQueryErrors={setQueryErrors} setHideError={setHideError} stats={queryStats} + isLoading={isLoading} onHideQuery={handleHideQuery} onRunQuery={handleRunQuery} + abortFetch={abortFetch} /> - {isLoading && } {showError && {error}} {showInstantQueryTip && } {warning && ( @@ -105,6 +107,7 @@ const CustomPanel: FC = () => { "vm-block_mobile": isMobile, })} > + {isLoading && }
{ const [period, setPeriod] = useState(periodState); const [queryError, setQueryError] = useState(""); - const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit); + const { logs, isLoading, error, fetchLogs, abortController } = useFetchLogs(serverUrl, query, limit); const { fetchLogHits, ...dataLogHits } = useFetchLogHits(serverUrl, query); const getPeriod = useCallback(() => { @@ -70,10 +69,15 @@ const ExploreLogs: FC = () => { setQuery(prev => `_stream: ${val === "other" ? "{}" : val} AND (${prev})`); }; - const handleUpdateQuery = () => { - setQuery(tmpQuery); - handleRunQuery(); - }; + const handleUpdateQuery = useCallback(() => { + if (isLoading || dataLogHits.isLoading) { + abortController.abort && abortController.abort(); + dataLogHits.abortController.abort && dataLogHits.abortController.abort(); + } else { + setQuery(tmpQuery); + handleRunQuery(); + } + }, [isLoading, dataLogHits.isLoading]); useEffect(() => { if (query) handleRunQuery(); @@ -93,8 +97,8 @@ const ExploreLogs: FC = () => { onChange={setTmpQuery} onChangeLimit={handleChangeLimit} onRun={handleUpdateQuery} + isLoading={isLoading || dataLogHits.isLoading} /> - {isLoading && } {error && {error}} {!error && ( { query={query} period={period} onApplyFilter={handleApplyFilter} - isLoading={isLoading ? false : dataLogHits.isLoading} /> )} - +
); }; diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx index 40b2cae0f4..506cf418d7 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx @@ -9,7 +9,7 @@ import { AlignedData } from "uplot"; import BarHitsChart from "../../../components/Chart/BarHitsChart/BarHitsChart"; import Alert from "../../../components/Main/Alert/Alert"; import { TimeParams } from "../../../types"; -import Spinner from "../../../components/Main/Spinner/Spinner"; +import LineLoader from "../../../components/Main/LineLoader/LineLoader"; interface Props { query: string; @@ -72,10 +72,7 @@ const ExploreLogsBarChart: FC = ({ logHits, period, error, isLoading, onA "vm-block_mobile": isMobile, })} > - {isLoading && } + {isLoading && } {!error && noDataMessage && (
{noDataMessage} diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx index 4c44dc37d3..da55841973 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx @@ -16,9 +16,11 @@ import TableLogs from "./TableLogs"; import GroupLogs from "../GroupLogs/GroupLogs"; import { DATE_TIME_FORMAT } from "../../../constants/date"; import { marked } from "marked"; +import LineLoader from "../../../components/Main/LineLoader/LineLoader"; export interface ExploreLogBodyProps { data: Logs[]; + isLoading: boolean; } enum DisplayType { @@ -33,7 +35,7 @@ const tabs = [ { label: "JSON", value: DisplayType.json, icon: }, ]; -const ExploreLogsBody: FC = ({ data }) => { +const ExploreLogsBody: FC = ({ data, isLoading }) => { const { isMobile } = useDeviceDetect(); const { timezone } = useTimeState(); const { setSearchParamsFromKeys } = useSearchParamsFromObject(); @@ -75,6 +77,7 @@ const ExploreLogsBody: FC = ({ data }) => { "vm-block_mobile": isMobile, })} > + {isLoading && }
void; onChangeLimit: (val: number) => void; onRun: () => void; @@ -20,6 +21,7 @@ const ExploreLogsHeader: FC = ({ query, limit, error, + isLoading, onChange, onChangeLimit, onRun, @@ -94,13 +96,16 @@ const ExploreLogsHeader: FC = ({ Documentation
-
+
diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss index 1bc7ab0b69..f2e0dbc10c 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss @@ -29,8 +29,18 @@ flex-grow: 1; } - &__execute { + &-execute { + position: relative; display: grid; + + &__text { + position: absolute; + + &_hidden { + position: relative; + visibility: hidden; + } + } } &-helpful { diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts index b9953f7adb..af784db47a 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts @@ -118,5 +118,6 @@ export const useFetchLogHits = (server: string, query: string) => { isLoading: Object.values(isLoading).some(s => s), error, fetchLogHits, + abortController: abortControllerRef.current }; }; diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts index e789f8e6a3..36dd4a8882 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts @@ -81,5 +81,6 @@ export const useFetchLogs = (server: string, query: string, limit: number) => { isLoading: Object.values(isLoading).some(s => s), error, fetchLogs, + abortController: abortControllerRef.current }; }; diff --git a/docs/VictoriaLogs/CHANGELOG.md b/docs/VictoriaLogs/CHANGELOG.md index 7cb34f2218..f0a66a5bc5 100644 --- a/docs/VictoriaLogs/CHANGELOG.md +++ b/docs/VictoriaLogs/CHANGELOG.md @@ -17,6 +17,7 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta * FEATURE: add support for forced merge. See [these docs](https://docs.victoriametrics.com/victorialogs/#forced-merge). * FEATURE: skip empty log fields in query results, since they are treated as non-existing fields in [VictoriaLogs data model](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model). +* FEATURE: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): add the ability to cancel running queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7097). * BUGFIX: avoid possible panic when logs for a new day are ingested during execution of concurrent queries. * BUGFIX: avoid panic at `lib/logstorage.(*blockResultColumn).forEachDictValue()` when [stats with additional filters](https://docs.victoriametrics.com/victorialogs/logsql/#stats-with-additional-filters). The panic has been introduced in [v0.33.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.33.0-victorialogs) in [this commit](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/a350be48b68330ee1a487e1fb09b002d3be45163). diff --git a/docs/changelog/CHANGELOG.md b/docs/changelog/CHANGELOG.md index 4670432dce..74f5d0273b 100644 --- a/docs/changelog/CHANGELOG.md +++ b/docs/changelog/CHANGELOG.md @@ -24,6 +24,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent/) and [Single-node VictoriaMetrics](https://docs.victoriametrics.com/): add support of [exponential histograms](https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram) ingested via [OpenTelemetry protocol for metrics](https://docs.victoriametrics.com/#sending-data-via-opentelemetry). Such histograms will be automatically converted to [VictoriaMetrics histogram format](https://valyala.medium.com/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350). See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6354). * FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/), `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/) and [vmagent](https://docs.victoriametrics.com/vmagent/): disable stream processing mode for data [ingested via InfluxDB](https://docs.victoriametrics.com/#how-to-send-data-from-influxdb-compatible-agents-such-as-telegraf) HTTP endpoints by default. With this change, the data is processed in batches (see `-influx.maxRequestSize`) and user will get parsing errors immediately as they happen. This also improves users' experience and resiliency against thundering herd problems caused by clients without backoff policies like telegraf. To enable stream mode back, pass HTTP header `Stream-Mode: "1"` with each request. For data sent via TCP and UDP (see `-influxListenAddr`) protocols streaming processing remains enabled. * FEATURE: [vmgateway](https://docs.victoriametrics.com/vmgateway/): allow parsing `scope` claim parsing in array format. This is useful for cases when identity provider does encode claims in array format. +* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to cancel running queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7097). * BUGFIX: [vmgateway](https://docs.victoriametrics.com/vmgateway/): fix possible panic during parsing of a token without `vm_access` claim. This issue was introduced in v1.104.0.