feat: add the option to see the latest queries (#4718) (#4759)

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
Yury Molodov 2023-09-04 11:29:11 +02:00 committed by Aliaksandr Valialkin
parent 7e92821f94
commit 30db332866
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
6 changed files with 188 additions and 7 deletions

View file

@ -1,6 +1,6 @@
@use "src/styles/variables" as *;
$padding-modal: 22px;
$padding-modal: $padding-medium;
.vm-modal {
position: fixed;
@ -43,9 +43,6 @@ $padding-modal: 22px;
}
&-content {
display: grid;
grid-template-rows: auto 1fr;
align-items: flex-start;
background: $color-background-block;
box-shadow: 0 0 24px rgba($color-black, 0.07);
border-radius: $border-radius-small;

View file

@ -22,6 +22,7 @@ import { arrayEquals } from "../../../utils/array";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import { QueryStats } from "../../../api/types";
import { usePrettifyQuery } from "./hooks/usePrettifyQuery";
import QueryHistoryList from "../QueryHistory/QueryHistoryList";
export interface QueryConfiguratorProps {
queryErrors: string[];
@ -51,18 +52,25 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
const [stateQuery, setStateQuery] = useState(query || []);
const [hideQuery, setHideQuery] = useState<number[]>([]);
const [awaitStateQuery, setAwaitStateQuery] = useState(false);
const prevStateQuery = usePrevious(stateQuery) as (undefined | string[]);
const getPrettifiedQuery = usePrettifyQuery();
const updateHistory = () => {
queryDispatch({
type: "SET_QUERY_HISTORY", payload: stateQuery.map((q, i) => {
type: "SET_QUERY_HISTORY",
payload: stateQuery.map((q, i) => {
const h = queryHistory[i] || { values: [] };
const queryEqual = q === h.values[h.values.length - 1];
const newValues = !queryEqual && q ? [...h.values, q] : h.values;
// limit the history
if (newValues.length > 25) newValues.shift();
return {
index: h.values.length - Number(queryEqual),
values: !queryEqual && q ? [...h.values, q] : h.values
values: newValues
};
})
});
@ -99,6 +107,11 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
setStateQuery(prev => prev.map((q, i) => i === index ? value : q));
};
const handleSelectHistory = (value: string, index: number) => {
handleChangeQuery(value, index);
setAwaitStateQuery(true);
};
const handleHistoryChange = (step: number, indexQuery: number) => {
const { index, values } = queryHistory[indexQuery];
const newIndexHistory = index + step;
@ -149,6 +162,13 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
onHideQuery(hideQuery);
}, [hideQuery]);
useEffect(() => {
if (awaitStateQuery) {
handleRunQuery();
setAwaitStateQuery(false);
}
}, [stateQuery, awaitStateQuery]);
return <div
className={classNames({
"vm-query-configurator": true,
@ -223,6 +243,10 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
<div className="vm-query-configurator-settings">
<AdditionalSettings/>
<div className="vm-query-configurator-settings__buttons">
<QueryHistoryList
history={queryHistory}
handleSelectQuery={handleSelectHistory}
/>
{stateQuery.length < MAX_QUERY_FIELDS && (
<Button
variant="outlined"

View file

@ -40,7 +40,7 @@
&__buttons {
flex-grow: 1;
display: grid;
grid-template-columns: repeat(2, auto);
grid-template-columns: repeat(3, auto);
gap: $padding-small;
justify-content: flex-end;
}

View file

@ -0,0 +1,114 @@
import React, { FC, useMemo } from "preact/compat";
import Button from "../../../components/Main/Button/Button";
import { ClockIcon, CopyIcon, PlayCircleOutlineIcon } from "../../../components/Main/Icons";
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
import { QueryHistory } from "../../../state/query/reducer";
import useBoolean from "../../../hooks/useBoolean";
import Modal from "../../../components/Main/Modal/Modal";
import "./style.scss";
import Tabs from "../../../components/Main/Tabs/Tabs";
import { useState } from "react";
import useCopyToClipboard from "../../../hooks/useCopyToClipboard";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import classNames from "classnames";
interface QueryHistoryProps {
history: QueryHistory[];
handleSelectQuery: (query: string, index: number) => void
}
const QueryHistoryList: FC<QueryHistoryProps> = ({ history, handleSelectQuery }) => {
const { isMobile } = useDeviceDetect();
const copyToClipboard = useCopyToClipboard();
const {
value: openModal,
setTrue: handleOpenModal,
setFalse: handleCloseModal,
} = useBoolean(false);
const [activeTab, setActiveTab] = useState("0");
const tabs = useMemo(() => history.map((item, i) => ({
value: `${i}`,
label: `Query ${i+1}`,
})), [history]);
const queries = useMemo(() => {
const historyItem = history[+activeTab];
return historyItem ? historyItem.values.filter(q => q).reverse() : [];
}, [activeTab, history]);
const handleCopyQuery = (value: string) => async () => {
await copyToClipboard(value, "Query has been copied");
};
const handleRunQuery = (value: string, index: number) => () => {
handleSelectQuery(value, index);
handleCloseModal();
};
return (
<>
<Tooltip title={"Show history"}>
<Button
color="primary"
variant="text"
onClick={handleOpenModal}
startIcon={<ClockIcon/>}
/>
</Tooltip>
{openModal && (
<Modal
title={"Query history"}
onClose={handleCloseModal}
>
<div className="vm-query-history">
<div
className={classNames({
"vm-query-history__tabs": true,
"vm-section-header__tabs": true,
"vm-query-history__tabs_mobile": isMobile,
})}
>
<Tabs
activeItem={activeTab}
items={tabs}
onChange={setActiveTab}
/>
</div>
<div className="vm-query-history-list">
{queries.map((query, index) => (
<div
className="vm-query-history-list-item"
key={index}
>
<span className="vm-query-history-list-item__value">{query}</span>
<div className="vm-query-history-list-item__buttons">
<Tooltip title={"Execute query"}>
<Button
size="small"
variant="text"
onClick={handleRunQuery(query, +activeTab)}
startIcon={<PlayCircleOutlineIcon/>}
/>
</Tooltip>
<Tooltip title={"Copy query"}>
<Button
size="small"
variant="text"
onClick={handleCopyQuery(query)}
startIcon={<CopyIcon/>}
/>
</Tooltip>
</div>
</div>
))}
</div>
</div>
</Modal>
)}
</>
);
};
export default QueryHistoryList;

View file

@ -0,0 +1,45 @@
@use "src/styles/variables" as *;
.vm-query-history {
max-width: 80vw;
min-width: 40vw;
&__tabs {
margin: (-$padding-medium) (-$padding-medium) 0;
padding: 0 $padding-medium;
&_mobile {
margin: (-$padding-global) (-$padding-medium) 0;
}
}
&-list {
display: grid;
align-items: flex-start;
&-item {
display: grid;
grid-template-columns: 1fr auto;
gap: $padding-small;
align-items: center;
margin: 0 (-$padding-medium) 0;
padding: $padding-small calc($padding-medium + $padding-small);
border-bottom: $border-divider;
&:first-child {
border-top: $border-divider;
}
&__value {
white-space: pre-wrap;
overflow-wrap: anywhere;
font-family: $font-family-monospace;
}
&__buttons {
display: flex;
gap: $padding-small;
}
}
}
}

View file

@ -26,6 +26,7 @@ The following `tip` changes can be tested by building VictoriaMetrics components
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add support for numbers with underscore delimiters such as `1_234_567_890` and `1.234_567_890`. These numbers are easier to read than `1234567890` and `1.234567890`.
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add support for server-side copy of existing backups. See [these docs](https://docs.victoriametrics.com/vmbackup.html#server-side-copy-of-the-existing-backup) for details.
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the option to see the latest 25 queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4718).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add ability to set `member num` label for all the metrics scraped by a particular `vmagent` instance in [a cluster of vmagents](https://docs.victoriametrics.com/vmagent.html#scraping-big-number-of-targets) via `-promscrape.cluster.memberLabel` command-line flag. See [these docs](https://docs.victoriametrics.com/vmagent.html#scraping-big-number-of-targets) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4247).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not log `unexpected EOF` when reading incoming metrics, since this error is expected and is handled during metrics' parsing. This reduces the amounts of noisy logs. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4817).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): retry failed write request on the closed connection immediately, without waiting for backoff. This should improve data delivery speed and reduce amount of error logs emitted by vmagent when using idle connections. See related [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4139).