Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useMemo, useState } from "preact/compat";
import { FC, useEffect, useMemo, useRef, useState } from "preact/compat";
import "./style.scss";
import "uplot/dist/uPlot.min.css";
import { AlignedData } from "uplot";
Expand All @@ -11,6 +11,7 @@ import { calculateTotalHits } from "../../../utils/logs";
import BarHitsStats from "./BarHitsStats/BarHitsStats";
import { HitsChartAlert } from "../../../pages/QueryPage/HitsPanel/hooks/useHitsChartAlert";
import Alert from "../../Main/Alert/Alert";
import { timeParamsToDateRange } from "../../../utils/time";

interface Props {
logHits: LogHits[];
Expand Down Expand Up @@ -45,6 +46,58 @@ const BarHitsChart: FC<Props> = ({
const isHitsMode = graphOptions.queryMode === GRAPH_QUERY_MODE.hits;
const totalHits = useMemo(() => calculateTotalHits(logHits), [logHits]);

const currentPeriodRef = useRef(period);
const chartPeriodChangeRef = useRef(false);
const periodChangeTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
const periodBeforeInteractionRef = useRef<TimeParams>();
const [prevPeriod, setPrevPeriod] = useState<TimeParams>();

const handleChangePeriod = (nextPeriod: TimePeriod) => {
chartPeriodChangeRef.current = true;

if (periodChangeTimeoutRef.current === undefined) {
periodBeforeInteractionRef.current = currentPeriodRef.current;
}

clearTimeout(periodChangeTimeoutRef.current);

periodChangeTimeoutRef.current = setTimeout(() => {
setPrevPeriod(periodBeforeInteractionRef.current);
periodBeforeInteractionRef.current = undefined;
periodChangeTimeoutRef.current = undefined;
chartPeriodChangeRef.current = false;
}, 500);

setPeriod(nextPeriod);
};


const resetPeriodInteraction = () => {
clearTimeout(periodChangeTimeoutRef.current);
periodChangeTimeoutRef.current = undefined;
periodBeforeInteractionRef.current = undefined;
setPrevPeriod(undefined);
};

const handleRevertPeriod = () => {
if (!prevPeriod) return;

setPeriod(timeParamsToDateRange(prevPeriod));
resetPeriodInteraction();
};

useEffect(() => {
currentPeriodRef.current = period;

if (chartPeriodChangeRef.current) {
Comment thread
Loori-R marked this conversation as resolved.
return;
}

resetPeriodInteraction();
}, [period]);

useEffect(() => resetPeriodInteraction, []);

return (
<div className="vm-bar-hits-chart__wrapper">
<div className="vm-bar-hits-chart-header">
Expand All @@ -60,6 +113,8 @@ const BarHitsChart: FC<Props> = ({
query={query}
isHitsMode={isHitsMode}
isOverview={isOverview}
prevPeriod={prevPeriod}
onRevertPeriod={handleRevertPeriod}
onChange={setGraphOptions}
/>
</div>
Expand All @@ -76,7 +131,7 @@ const BarHitsChart: FC<Props> = ({
totalHits={totalHits}
data={_data}
period={period}
setPeriod={setPeriod}
setPeriod={handleChangePeriod}
graphOptions={graphOptions}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import "./style.scss";
import useStateSearchParams from "../../../../hooks/useStateSearchParams";
import { useSearchParams } from "react-router-dom";
import Button from "../../../Main/Button/Button";
import { KeyboardIcon, MoreIcon, VisibilityIcon, VisibilityOffIcon } from "../../../Main/Icons";
import {
ArrowBackIcon,
KeyboardIcon,
MoreIcon,
VisibilityIcon,
VisibilityOffIcon
} from "../../../Main/Icons";
import Tooltip from "../../../Main/Tooltip/Tooltip";
import ShortcutKeys from "../../../Main/ShortcutKeys/ShortcutKeys";
import { useCallback } from "react";
Expand All @@ -19,17 +25,23 @@ import { useHitsChartConfig } from "../../../../pages/QueryPage/HitsPanel/hooks/
import { useExtraFilters } from "../../../ExtraFilters/hooks/useExtraFilters";
import { useFetchFieldNames } from "../../../../pages/OverviewPage/hooks/useFetchFieldNames";
import { getDefaultIntervalOption, getIntervalOptions } from "../../../../utils/intervals";
import { nanosToIsoString, vmDate } from "../../../../utils/time";
import { useTimePeriod } from "../../../../pages/QueryPage/hooks/useTimePeriod";
import usePrevious from "../../../../hooks/usePrevious";
import { TimeParams } from "../../../../types";
import { DATE_TIME_FORMAT } from "../../../../constants/date";
import { useTimeState } from "../../../../state/time/TimeStateContext";

interface Props {
query?: string;
isHitsMode?: boolean;
isOverview?: boolean;
prevPeriod?: TimeParams;
onRevertPeriod: () => void
onChange: (options: GraphOptions) => void;
}

const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, onChange }) => {
const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, prevPeriod, onRevertPeriod, onChange }) => {
const { isMobile } = useDeviceDetect();
const {
value: openList,
Expand All @@ -40,6 +52,7 @@ const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, onChange })
const [searchParams, setSearchParams] = useSearchParams();

const { topHits, groupFieldHits, step } = useHitsChartConfig();
const { timezone } = useTimeState();

const { extraParams } = useExtraFilters();
const { period: { start, end } } = useTimePeriod();
Expand All @@ -55,6 +68,14 @@ const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, onChange })
const [cumulative, setCumulative] = useStateSearchParams(false, "cumulative");
const [hideChart, setHideChart] = useStateSearchParams(false, "hide_chart");

const prevPeriodFormatted = useMemo(() => {
if (!prevPeriod) return;

const startIso = nanosToIsoString(prevPeriod.start);
const endIso = nanosToIsoString(prevPeriod.end);
return `${vmDate(startIso).tz().format(DATE_TIME_FORMAT)} - ${vmDate(endIso).tz().format(DATE_TIME_FORMAT)}`;
}, [prevPeriod, timezone]);

const options: GraphOptions = useMemo(() => ({
graphStyle: GRAPH_STYLES.BAR,
queryMode,
Expand Down Expand Up @@ -180,6 +201,20 @@ const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, onChange })
/>
</div>
)}

{prevPeriod && (
<Tooltip title={`Back to previous range: ${prevPeriodFormatted}`}>
<div
className="vm-bar-hits-options-item"
onClick={onRevertPeriod}
>
<button className="vm-select-limits-button vm-bar-hits-options-item_timerange">
<ArrowBackIcon/>
Back to prev range
</button>
</div>
</Tooltip>
)}
</div>

<div className="vm-bar-hits-options-item vm-bar-hits-options-item_switch">
Expand Down Expand Up @@ -213,7 +248,7 @@ const BarHitsOptions: FC<Props> = ({ query, isHitsMode, isOverview, onChange })
className={classNames({
"vm-bar-hits-options": true,
"vm-bar-hits-options_mobile": isMobile,
"vm-bar-hits-options_hidden": hideChart,
"vm-bar-hits-options_hidden": hideChart,
})}
>
{!isMobile && !hideChart && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
&_switch {
padding-right: $padding-global;
}

&_timerange {
gap: calc($padding-small / 4);
padding-inline: calc($padding-small / 2) $padding-small;
grid-template-columns: 16px 1fr;
min-height: 24px;
}
}

&_mobile {
Expand Down
9 changes: 9 additions & 0 deletions app/vmui/packages/vmui/src/components/Main/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ export const ArrowDropDownIcon = () => (
</svg>
);

export const ArrowBackIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20z"></path>
</svg>
);

export const ClockIcon = () => (
<svg
viewBox="0 0 24 24"
Expand Down
1 change: 1 addition & 0 deletions docs/victorialogs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ according to the following docs:
* [How to build vlagent from source code](https://docs.victoriametrics.com/victorialogs/vlagent/#building-from-source-code)

## tip
* FEATURE: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): add a `Back` action to the Hits chart for restoring the previous time range after zooming or panning the chart. See [#1535](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1535).

* BUGFIX: [cluster version](https://docs.victoriametrics.com/victorialogs/cluster/): evenly spread rerouted data across available `vlstorage` nodes. Previously, healthy nodes adjacent to unavailable nodes in the `-storageNode` list could receive much more data, resulting in uneven resource usage. See [#1548](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1548).
* BUGFIX: [data ingestion](https://docs.victoriametrics.com/victorialogs/data-ingestion/) and [querying](https://docs.victoriametrics.com/victorialogs/querying/): properly handle logs containing duplicate [stream field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#stream-fields) names. Previously, [v1.52.0](https://github.com/VictoriaMetrics/VictoriaLogs/releases/tag/v1.52.0) could panic when ingesting such logs in single-node VictoriaLogs, drop them during ingestion in VictoriaLogs cluster, or panic when querying such data written by earlier releases. See [#1603](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1603) and [#1604](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1604).
Expand Down
Loading