vmui/logs: fix display of hits chart (#7167)

### Describe Your Changes

Fixed the display of hits chart in VictoriaLogs.
See #7133

### Checklist

The following checks are **mandatory**:

- [ ] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).
This commit is contained in:
Yury Molodov 2024-10-18 02:28:23 +02:00 committed by GitHub
parent 064b9a6314
commit 36a86c3aaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 20 deletions

View file

@ -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<Props> = ({ 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]);

View file

@ -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,

View file

@ -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 };
};

View file

@ -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