diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogs.tsx b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogs.tsx index 8b289f4f51..f7d2947730 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogs.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogs.tsx @@ -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(periodState); const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit); const { fetchLogHits, ...dataLogHits } = useFetchLogHits(serverUrl, query); const [queryError, setQueryError] = useState(""); - 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 && } {error && {error}} - + {!error && ( + + )} 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 1b367d9cad..7609095ea1 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx @@ -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 = ({ logHits, error, loaded }) => { +const ExploreLogsBarChart: FC = ({ logHits, period, error }) => { const { isMobile } = useDeviceDetect(); - const { period } = useTimeState(); const timeDispatch = useTimeDispatch(); const data = useMemo(() => { @@ -56,13 +56,13 @@ const ExploreLogsBarChart: FC = ({ logHits, error, loaded }) => { "vm-block_mobile": isMobile, })} > - {!error && loaded && noDataMessage && ( + {!error && noDataMessage && (
{noDataMessage}
)} - {error && loaded && noDataMessage && ( + {error && noDataMessage && (
{error}
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 f213b9a024..6405d1ec14 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx @@ -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: }, ]; -const ExploreLogsBody: FC = ({ data, loaded, markdownParsing }) => { +const ExploreLogsBody: FC = ({ data, markdownParsing }) => { const { isMobile } = useDeviceDetect(); const { timezone } = useTimeState(); const { setSearchParamsFromKeys } = useSearchParamsFromObject(); @@ -109,11 +108,7 @@ const ExploreLogsBody: FC = ({ data, loaded, markdownParsin "vm-explore-logs-body__table_mobile": isMobile, })} > - {!data.length && ( -
- {loaded ? "No logs found" : "Run query to see logs"} -
- )} + {!data.length &&
No logs found
} {!!data.length && ( <> {activeTab === DisplayType.table && ( 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 b6a709c7eb..42eb0cefac 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts @@ -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([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); @@ -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, 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 00801cc2fa..f913149916 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts @@ -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([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); @@ -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, diff --git a/docs/VictoriaLogs/CHANGELOG.md b/docs/VictoriaLogs/CHANGELOG.md index 6bcf6dabb6..4c0fa12384 100644 --- a/docs/VictoriaLogs/CHANGELOG.md +++ b/docs/VictoriaLogs/CHANGELOG.md @@ -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