mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui/logs: fix the update of the relative time range (#6517)
### Describe Your Changes - Fixed the update of the relative time range when `Execute Query` is clicked - Optimized server requests: now, if an error occurs in the `/query` request, the `/hits` request will not be executed. #6345 (duplicates: #6440, #6312)
This commit is contained in:
parent
6652fb630f
commit
43342745ac
6 changed files with 67 additions and 51 deletions
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useEffect } from "preact/compat";
|
||||
import React, { FC, useCallback, useEffect } from "preact/compat";
|
||||
import ExploreLogsBody from "./ExploreLogsBody/ExploreLogsBody";
|
||||
import useStateSearchParams from "../../hooks/useStateSearchParams";
|
||||
import useSearchParamsFromObject from "../../hooks/useSearchParamsFromObject";
|
||||
|
@ -8,45 +8,54 @@ import Spinner from "../../components/Main/Spinner/Spinner";
|
|||
import Alert from "../../components/Main/Alert/Alert";
|
||||
import ExploreLogsHeader from "./ExploreLogsHeader/ExploreLogsHeader";
|
||||
import "./style.scss";
|
||||
import { ErrorTypes } from "../../types";
|
||||
import { ErrorTypes, TimeParams } from "../../types";
|
||||
import { useState } from "react";
|
||||
import { useTimeState } from "../../state/time/TimeStateContext";
|
||||
import { getFromStorage, saveToStorage } from "../../utils/storage";
|
||||
import ExploreLogsBarChart from "./ExploreLogsBarChart/ExploreLogsBarChart";
|
||||
import { useFetchLogHits } from "./hooks/useFetchLogHits";
|
||||
import { LOGS_ENTRIES_LIMIT } from "../../constants/logs";
|
||||
import { getTimeperiodForDuration, relativeTimeOptions } from "../../utils/time";
|
||||
|
||||
const storageLimit = Number(getFromStorage("LOGS_LIMIT"));
|
||||
const defaultLimit = isNaN(storageLimit) ? LOGS_ENTRIES_LIMIT : storageLimit;
|
||||
|
||||
const ExploreLogs: FC = () => {
|
||||
const { serverUrl } = useAppState();
|
||||
const { duration, relativeTime, period } = useTimeState();
|
||||
const { duration, relativeTime, period: periodState } = useTimeState();
|
||||
const { setSearchParamsFromKeys } = useSearchParamsFromObject();
|
||||
|
||||
const [limit, setLimit] = useStateSearchParams(defaultLimit, "limit");
|
||||
const [query, setQuery] = useStateSearchParams("*", "query");
|
||||
const [period, setPeriod] = useState<TimeParams>(periodState);
|
||||
const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit);
|
||||
const { fetchLogHits, ...dataLogHits } = useFetchLogHits(serverUrl, query);
|
||||
const [queryError, setQueryError] = useState<ErrorTypes | string>("");
|
||||
const [loaded, isLoaded] = useState(false);
|
||||
const [markdownParsing, setMarkdownParsing] = useState(getFromStorage("LOGS_MARKDOWN") === "true");
|
||||
|
||||
const getPeriod = useCallback(() => {
|
||||
const relativeTimeOpts = relativeTimeOptions.find(d => d.id === relativeTime);
|
||||
if (!relativeTimeOpts) return periodState;
|
||||
const { duration, until } = relativeTimeOpts;
|
||||
return getTimeperiodForDuration(duration, until());
|
||||
}, [periodState, relativeTime]);
|
||||
|
||||
const handleRunQuery = () => {
|
||||
if (!query) {
|
||||
setQueryError(ErrorTypes.validQuery);
|
||||
return;
|
||||
}
|
||||
|
||||
fetchLogs().then(() => {
|
||||
isLoaded(true);
|
||||
});
|
||||
fetchLogHits();
|
||||
const newPeriod = getPeriod();
|
||||
setPeriod(newPeriod);
|
||||
fetchLogs(newPeriod).then(() => {
|
||||
fetchLogHits(newPeriod);
|
||||
}).catch(e => e);
|
||||
|
||||
setSearchParamsFromKeys( {
|
||||
query,
|
||||
"g0.range_input": duration,
|
||||
"g0.end_input": period.date,
|
||||
"g0.end_input": newPeriod.date,
|
||||
"g0.relative_time": relativeTime || "none",
|
||||
});
|
||||
};
|
||||
|
@ -64,7 +73,7 @@ const ExploreLogs: FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (query) handleRunQuery();
|
||||
}, [period]);
|
||||
}, [periodState]);
|
||||
|
||||
useEffect(() => {
|
||||
setQueryError("");
|
||||
|
@ -84,14 +93,15 @@ const ExploreLogs: FC = () => {
|
|||
/>
|
||||
{isLoading && <Spinner />}
|
||||
{error && <Alert variant="error">{error}</Alert>}
|
||||
<ExploreLogsBarChart
|
||||
query={query}
|
||||
loaded={loaded}
|
||||
{...dataLogHits}
|
||||
/>
|
||||
{!error && (
|
||||
<ExploreLogsBarChart
|
||||
query={query}
|
||||
period={period}
|
||||
{...dataLogHits}
|
||||
/>
|
||||
)}
|
||||
<ExploreLogsBody
|
||||
data={logs}
|
||||
loaded={loaded}
|
||||
markdownParsing={markdownParsing}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -4,22 +4,22 @@ import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
|||
import classNames from "classnames";
|
||||
import { LogHits } from "../../../api/types";
|
||||
import dayjs from "dayjs";
|
||||
import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
|
||||
import { useTimeDispatch } from "../../../state/time/TimeStateContext";
|
||||
import { AlignedData } from "uplot";
|
||||
import BarHitsChart from "../../../components/Chart/BarHitsChart/BarHitsChart";
|
||||
import Alert from "../../../components/Main/Alert/Alert";
|
||||
import { TimeParams } from "../../../types";
|
||||
|
||||
interface Props {
|
||||
query: string;
|
||||
logHits: LogHits[];
|
||||
period: TimeParams;
|
||||
error?: string;
|
||||
isLoading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
const ExploreLogsBarChart: FC<Props> = ({ logHits, error, loaded }) => {
|
||||
const ExploreLogsBarChart: FC<Props> = ({ logHits, period, error }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const { period } = useTimeState();
|
||||
const timeDispatch = useTimeDispatch();
|
||||
|
||||
const data = useMemo(() => {
|
||||
|
@ -56,13 +56,13 @@ const ExploreLogsBarChart: FC<Props> = ({ logHits, error, loaded }) => {
|
|||
"vm-block_mobile": isMobile,
|
||||
})}
|
||||
>
|
||||
{!error && loaded && noDataMessage && (
|
||||
{!error && noDataMessage && (
|
||||
<div className="vm-explore-logs-chart__empty">
|
||||
<Alert variant="info">{noDataMessage}</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && loaded && noDataMessage && (
|
||||
{error && noDataMessage && (
|
||||
<div className="vm-explore-logs-chart__empty">
|
||||
<Alert variant="error">{error}</Alert>
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,6 @@ import { marked } from "marked";
|
|||
|
||||
export interface ExploreLogBodyProps {
|
||||
data: Logs[];
|
||||
loaded?: boolean;
|
||||
markdownParsing: boolean;
|
||||
}
|
||||
|
||||
|
@ -35,7 +34,7 @@ const tabs = [
|
|||
{ label: "JSON", value: DisplayType.json, icon: <CodeIcon/> },
|
||||
];
|
||||
|
||||
const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded, markdownParsing }) => {
|
||||
const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, markdownParsing }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const { timezone } = useTimeState();
|
||||
const { setSearchParamsFromKeys } = useSearchParamsFromObject();
|
||||
|
@ -109,11 +108,7 @@ const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded, markdownParsin
|
|||
"vm-explore-logs-body__table_mobile": isMobile,
|
||||
})}
|
||||
>
|
||||
{!data.length && (
|
||||
<div className="vm-explore-logs-body__empty">
|
||||
{loaded ? "No logs found" : "Run query to see logs"}
|
||||
</div>
|
||||
)}
|
||||
{!data.length && <div className="vm-explore-logs-body__empty">No logs found</div>}
|
||||
{!!data.length && (
|
||||
<>
|
||||
{activeTab === DisplayType.table && (
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { useCallback, useMemo, useRef, useState } from "preact/compat";
|
||||
import { getLogHitsUrl } from "../../../api/logs";
|
||||
import { ErrorTypes } from "../../../types";
|
||||
import { ErrorTypes, TimeParams } from "../../../types";
|
||||
import { LogHits } from "../../../api/types";
|
||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||
import dayjs from "dayjs";
|
||||
import { LOGS_BARS_VIEW } from "../../../constants/logs";
|
||||
|
||||
export const useFetchLogHits = (server: string, query: string) => {
|
||||
const { period } = useTimeState();
|
||||
const [logHits, setLogHits] = useState<LogHits[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorTypes | string>();
|
||||
|
@ -15,13 +13,14 @@ export const useFetchLogHits = (server: string, query: string) => {
|
|||
|
||||
const url = useMemo(() => getLogHitsUrl(server), [server]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
const getOptions = (query: string, period: TimeParams, signal: AbortSignal) => {
|
||||
const start = dayjs(period.start * 1000);
|
||||
const end = dayjs(period.end * 1000);
|
||||
const totalSeconds = end.diff(start, "milliseconds");
|
||||
const step = Math.ceil(totalSeconds / LOGS_BARS_VIEW) || 1;
|
||||
|
||||
return {
|
||||
signal,
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
query: query.trim(),
|
||||
|
@ -30,30 +29,34 @@ export const useFetchLogHits = (server: string, query: string) => {
|
|||
end: end.toISOString(),
|
||||
})
|
||||
};
|
||||
}, [query, period]);
|
||||
};
|
||||
|
||||
const fetchLogHits = useCallback(async () => {
|
||||
const fetchLogHits = useCallback(async (period: TimeParams) => {
|
||||
abortControllerRef.current.abort();
|
||||
abortControllerRef.current = new AbortController();
|
||||
const { signal } = abortControllerRef.current;
|
||||
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { ...options, signal });
|
||||
const options = getOptions(query, period, signal);
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok || !response.body) {
|
||||
const text = await response.text();
|
||||
setError(text);
|
||||
setLogHits([]);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
return Promise.reject(new Error(text));
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const hits = data?.hits as LogHits[];
|
||||
if (!hits) {
|
||||
setError("Error: No 'hits' field in response");
|
||||
return;
|
||||
const error = "Error: No 'hits' field in response";
|
||||
setError(error);
|
||||
return Promise.reject(new Error(error));
|
||||
}
|
||||
|
||||
setLogHits(hits);
|
||||
|
@ -63,9 +66,11 @@ export const useFetchLogHits = (server: string, query: string) => {
|
|||
console.error(e);
|
||||
setLogHits([]);
|
||||
}
|
||||
return Promise.reject(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
// setIsLoading(false);
|
||||
}, [url, options]);
|
||||
}, [url, query]);
|
||||
|
||||
return {
|
||||
logHits,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { useCallback, useMemo, useRef, useState } from "preact/compat";
|
||||
import { getLogsUrl } from "../../../api/logs";
|
||||
import { ErrorTypes } from "../../../types";
|
||||
import { ErrorTypes, TimeParams } from "../../../types";
|
||||
import { Logs } from "../../../api/types";
|
||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const useFetchLogs = (server: string, query: string, limit: number) => {
|
||||
const { period } = useTimeState();
|
||||
const [logs, setLogs] = useState<Logs[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorTypes | string>();
|
||||
|
@ -14,7 +12,8 @@ export const useFetchLogs = (server: string, query: string, limit: number) => {
|
|||
|
||||
const url = useMemo(() => getLogsUrl(server), [server]);
|
||||
|
||||
const options = useMemo(() => ({
|
||||
const getOptions = (query: string, period: TimeParams, limit: number, signal: AbortSignal) => ({
|
||||
signal,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/stream+json",
|
||||
|
@ -25,7 +24,7 @@ export const useFetchLogs = (server: string, query: string, limit: number) => {
|
|||
start: dayjs(period.start * 1000).tz().toISOString(),
|
||||
end: dayjs(period.end * 1000).tz().toISOString()
|
||||
})
|
||||
}), [query, limit, period]);
|
||||
});
|
||||
|
||||
const parseLineToJSON = (line: string): Logs | null => {
|
||||
try {
|
||||
|
@ -35,22 +34,24 @@ export const useFetchLogs = (server: string, query: string, limit: number) => {
|
|||
}
|
||||
};
|
||||
|
||||
const fetchLogs = useCallback(async () => {
|
||||
const fetchLogs = useCallback(async (period: TimeParams) => {
|
||||
abortControllerRef.current.abort();
|
||||
abortControllerRef.current = new AbortController();
|
||||
const { signal } = abortControllerRef.current;
|
||||
const limit = Number(options.body.get("limit"));
|
||||
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { ...options, signal });
|
||||
const options = getOptions(query, period, limit, signal);
|
||||
const response = await fetch(url, options);
|
||||
const text = await response.text();
|
||||
|
||||
if (!response.ok || !response.body) {
|
||||
setError(text);
|
||||
setLogs([]);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
return Promise.reject(new Error(text));
|
||||
}
|
||||
|
||||
const lines = text.split("\n").filter(line => line).slice(0, limit);
|
||||
|
@ -62,9 +63,12 @@ export const useFetchLogs = (server: string, query: string, limit: number) => {
|
|||
console.error(e);
|
||||
setLogs([]);
|
||||
}
|
||||
return Promise.reject(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [url, options]);
|
||||
}, [url, query, limit]);
|
||||
|
||||
return {
|
||||
logs,
|
||||
|
|
|
@ -21,6 +21,8 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
|
|||
|
||||
* FEATURE: add `-retention.maxDiskSpaceUsageBytes` command-line flag, which allows limiting disk space usage for [VictoriaLogs data](https://docs.victoriametrics.com/victorialogs/#storage) by automatic dropping the oldest per-day partitions if the storage disk space usage becomes bigger than the `-retention.maxDiskSpaceUsageBytes`. See [these docs](https://docs.victoriametrics.com/victorialogs/#retention-by-disk-space-usage).
|
||||
|
||||
* BUGFIX: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): fix the update of the relative time range when `Execute Query` is clicked. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6345).
|
||||
|
||||
## [v0.23.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.23.0-victorialogs)
|
||||
|
||||
Released at 2024-06-25
|
||||
|
|
Loading…
Reference in a new issue