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 506cf418d..326e9febd 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx @@ -1,4 +1,4 @@ -import React, { FC, useMemo } from "preact/compat"; +import React, { FC, useCallback, useMemo } from "preact/compat"; import "./style.scss"; import useDeviceDetect from "../../../hooks/useDeviceDetect"; import classNames from "classnames"; @@ -10,6 +10,7 @@ import BarHitsChart from "../../../components/Chart/BarHitsChart/BarHitsChart"; import Alert from "../../../components/Main/Alert/Alert"; import { TimeParams } from "../../../types"; import LineLoader from "../../../components/Main/LineLoader/LineLoader"; +import { getHitsTimeParams } from "../../../utils/logs"; interface Props { query: string; @@ -24,26 +25,43 @@ const ExploreLogsBarChart: FC = ({ logHits, period, error, isLoading, onA const { isMobile } = useDeviceDetect(); const timeDispatch = useTimeDispatch(); - const getXAxis = (timestamps: string[]): number[] => { - return (timestamps.map(t => t ? dayjs(t).unix() : null) - .filter(Boolean) as number[]) - .sort((a, b) => a - b); - }; - - const getYAxes = (logHits: LogHits[], timestamps: string[]) => { + const getYAxes = (logHits: LogHits[], timestamps: number[]) => { return logHits.map(hits => { - return timestamps.map(t => { - const index = hits.timestamps.findIndex(ts => ts === t); - return index === -1 ? null : hits.values[index] || null; + const timestampValueMap = new Map(); + hits.timestamps.forEach((ts, idx) => { + const unixTime = dayjs(ts).unix(); + timestampValueMap.set(unixTime, hits.values[idx] || null); }); + + return timestamps.map(t => timestampValueMap.get(t) || null); }); }; + const generateTimestamps = useCallback((date: dayjs.Dayjs) => { + const result: number[] = []; + const { start, end, step } = getHitsTimeParams(period); + const stepsToFirstTimestamp = Math.ceil(start.diff(date, "milliseconds") / step); + let firstTimestamp = date.add(stepsToFirstTimestamp * step, "milliseconds"); + + // If the first timestamp is before 'start', set it to 'start' + if (firstTimestamp.isBefore(start)) { + firstTimestamp = start.clone(); + } + + // Calculate the total number of steps from 'firstTimestamp' to 'end' + const totalSteps = Math.floor(end.diff(firstTimestamp, "milliseconds") / step); + + for (let i = 0; i <= totalSteps; i++) { + result.push(firstTimestamp.add(i * step, "milliseconds").unix()); + } + + return result; + }, [period]); + const data = useMemo(() => { if (!logHits.length) return [[], []] as AlignedData; - const timestamps = Array.from(new Set(logHits.map(l => l.timestamps).flat())); - const xAxis = getXAxis(timestamps); - const yAxes = getYAxes(logHits, timestamps); + const xAxis = generateTimestamps(dayjs(logHits[0].timestamps[0])); + const yAxes = getYAxes(logHits, xAxis); return [xAxis, ...yAxes] as AlignedData; }, [logHits]); 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 af784db47..c46d48c76 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts @@ -2,9 +2,8 @@ import { useCallback, useMemo, useRef, useState } from "preact/compat"; import { getLogHitsUrl } from "../../../api/logs"; import { ErrorTypes, TimeParams } from "../../../types"; import { LogHits } from "../../../api/types"; -import dayjs from "dayjs"; -import { LOGS_BARS_VIEW } from "../../../constants/logs"; import { useSearchParams } from "react-router-dom"; +import { getHitsTimeParams } from "../../../utils/logs"; export const useFetchLogHits = (server: string, query: string) => { const [searchParams] = useSearchParams(); @@ -17,10 +16,7 @@ export const useFetchLogHits = (server: string, query: string) => { const url = useMemo(() => getLogHitsUrl(server), [server]); 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; + const { start, end, step } = getHitsTimeParams(period); return { signal, diff --git a/app/vmui/packages/vmui/src/utils/logs.ts b/app/vmui/packages/vmui/src/utils/logs.ts index 13e3d246c..bcf970837 100644 --- a/app/vmui/packages/vmui/src/utils/logs.ts +++ b/app/vmui/packages/vmui/src/utils/logs.ts @@ -1,4 +1,16 @@ +import { TimeParams } from "../types"; +import dayjs from "dayjs"; +import { LOGS_BARS_VIEW } from "../constants/logs"; + export const getStreamPairs = (value: string): string[] => { const pairs = /^{.+}$/.test(value) ? value.slice(1, -1).split(",") : [value]; return pairs.filter(Boolean); }; + +export const getHitsTimeParams = (period: TimeParams) => { + 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 { start, end, step }; +}; diff --git a/docs/VictoriaLogs/CHANGELOG.md b/docs/VictoriaLogs/CHANGELOG.md index 42e079336..f9a40d7ca 100644 --- a/docs/VictoriaLogs/CHANGELOG.md +++ b/docs/VictoriaLogs/CHANGELOG.md @@ -20,6 +20,8 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta * FEATURE: improve performance for [`top`](https://docs.victoriametrics.com/victorialogs/logsql/#top-pipe), [`uniq`](https://docs.victoriametrics.com/victorialogs/logsql/#uniq-pipe) and [`field_values`](https://docs.victoriametrics.com/victorialogs/logsql/#field_values-pipe) pipes on systems with many CPU cores when it is applied to [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) with big number of unique values. For example, `_time:1d | top 5 (user_id)` should be executed much faster when `user_id` field contains millions of unique values. * FEATURE: improve performance for [`field_names` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#field_names-pipe) when it is applied to logs with hundreds of [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model). +* BUGFIX: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): fix display of hits chart. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7133). + ## [v0.36.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.36.0-victorialogs) Released at 2024-10-16