vmui: enhancements to top queries page (#4299)

* feat: improvement of the top queries page

* vmui/docs: enhancements to top queries page

* Apply suggestions from code review

---------

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
Yury Molodov 2023-05-11 22:47:32 +02:00 committed by GitHub
parent 9855b38da2
commit 1e4a9a8dfe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 65 additions and 11 deletions

View file

@ -11,7 +11,7 @@ import useDeviceDetect from "../../../hooks/useDeviceDetect";
export interface TopQueryPanelProps { export interface TopQueryPanelProps {
rows: TopQuery[], rows: TopQuery[],
title?: string, title?: string,
columns: {title?: string, key: (keyof TopQuery)}[], columns: {title?: string, key: (keyof TopQuery), sortBy?: (keyof TopQuery)}[],
defaultOrderBy?: keyof TopQuery, defaultOrderBy?: keyof TopQuery,
} }
const tabs = ["table", "JSON"].map((t, i) => ({ const tabs = ["table", "JSON"].map((t, i) => ({

View file

@ -3,14 +3,20 @@ import { TopQuery } from "../../../types";
import { getComparator, stableSort } from "../../CardinalityPanel/Table/helpers"; import { getComparator, stableSort } from "../../CardinalityPanel/Table/helpers";
import { TopQueryPanelProps } from "../TopQueryPanel/TopQueryPanel"; import { TopQueryPanelProps } from "../TopQueryPanel/TopQueryPanel";
import classNames from "classnames"; import classNames from "classnames";
import { ArrowDropDownIcon } from "../../../components/Main/Icons"; import { ArrowDropDownIcon, CopyIcon, PlayCircleOutlineIcon } from "../../../components/Main/Icons";
import Button from "../../../components/Main/Button/Button";
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
import { useSnack } from "../../../contexts/Snackbar";
import { Link } from "react-router-dom";
import router from "../../../router";
const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy }) => { const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy }) => {
const { showInfoMessage } = useSnack();
const [orderBy, setOrderBy] = useState<keyof TopQuery>(defaultOrderBy || "count"); const [orderBy, setOrderBy] = useState<keyof TopQuery>(defaultOrderBy || "count");
const [orderDir, setOrderDir] = useState<"asc" | "desc">("desc"); const [orderDir, setOrderDir] = useState<"asc" | "desc">("desc");
const sortedList = useMemo(() => stableSort(rows as [], getComparator(orderDir, orderBy)), const sortedList = useMemo(() => stableSort(rows as [], getComparator(orderDir, orderBy)) as TopQuery[],
[rows, orderBy, orderDir]); [rows, orderBy, orderDir]);
const onSortHandler = (key: keyof TopQuery) => { const onSortHandler = (key: keyof TopQuery) => {
@ -22,6 +28,12 @@ const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy })
onSortHandler(col); onSortHandler(col);
}; };
const createCopyHandler = ({ query }: TopQuery) => () => {
// TODO add useCopyToClipboard after merge https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4145
navigator.clipboard.writeText(query);
showInfoMessage({ text: "Query has been copied", type: "success" });
};
return ( return (
<table className="vm-table"> <table className="vm-table">
<thead className="vm-table-header"> <thead className="vm-table-header">
@ -29,7 +41,7 @@ const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy })
{columns.map((col) => ( {columns.map((col) => (
<th <th
className="vm-table-cell vm-table-cell_header vm-table-cell_sort" className="vm-table-cell vm-table-cell_header vm-table-cell_sort"
onClick={createSortHandler(col.key)} onClick={createSortHandler(col.sortBy || col.key)}
key={col.key} key={col.key}
> >
<div className="vm-table-cell__content"> <div className="vm-table-cell__content">
@ -46,6 +58,7 @@ const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy })
</div> </div>
</th> </th>
))} ))}
<th className="vm-table-cell vm-table-cell_header"/> {/* empty cell for actions */}
</tr> </tr>
</thead> </thead>
<tbody className="vm-table-body"> <tbody className="vm-table-body">
@ -62,6 +75,31 @@ const TopQueryTable:FC<TopQueryPanelProps> = ({ rows, columns, defaultOrderBy })
{row[col.key] || "-"} {row[col.key] || "-"}
</td> </td>
))} ))}
<td className="vm-table-cell vm-table-cell_no-padding">
<div className="vm-top-queries-panels__table-actions">
<Tooltip title={"Execute query"}>
<Link
to={`${router.home}?g0.expr=${encodeURIComponent(row.query)}`}
target="_blank"
rel="noreferrer"
>
<Button
variant="text"
size="small"
startIcon={<PlayCircleOutlineIcon/>}
/>
</Link>
</Tooltip>
<Tooltip title={"Copy query"}>
<Button
variant="text"
size="small"
startIcon={<CopyIcon/>}
onClick={createCopyHandler(row)}
/>
</Tooltip>
</div>
</td>
</tr> </tr>
))} ))}
</tbody> </tbody>

View file

@ -5,6 +5,7 @@ import { useMemo } from "preact/compat";
import { getTopQueries } from "../../../api/top-queries"; import { getTopQueries } from "../../../api/top-queries";
import { TopQueriesData } from "../../../types"; import { TopQueriesData } from "../../../types";
import { useTopQueriesState } from "../../../state/topQueries/TopQueriesStateContext"; import { useTopQueriesState } from "../../../state/topQueries/TopQueriesStateContext";
import { getDurationFromMilliseconds } from "../../../utils/time";
export const useFetchTopQueries = () => { export const useFetchTopQueries = () => {
const { serverUrl } = useAppState(); const { serverUrl } = useAppState();
@ -26,7 +27,7 @@ export const useFetchTopQueries = () => {
list.forEach(key => { list.forEach(key => {
const target = resp[key]; const target = resp[key];
if (Array.isArray(target)) { if (Array.isArray(target)) {
target.forEach(t => t.timeRangeHours = +(t.timeRangeSeconds/3600).toFixed(2)); target.forEach(t => t.timeRange = getDurationFromMilliseconds(t.timeRangeSeconds*1000));
} }
}); });
} }

View file

@ -147,7 +147,7 @@ const TopQueries: FC = () => {
title={"Most frequently executed queries"} title={"Most frequently executed queries"}
columns={[ columns={[
{ key: "query" }, { key: "query" },
{ key: "timeRangeHours", title: "time range, hours" }, { key: "timeRange", sortBy: "timeRangeSeconds", title: "Query Time Interval" },
{ key: "count" } { key: "count" }
]} ]}
/> />
@ -156,8 +156,8 @@ const TopQueries: FC = () => {
title={"Most heavy queries"} title={"Most heavy queries"}
columns={[ columns={[
{ key: "query" }, { key: "query" },
{ key: "avgDurationSeconds", title: "avg duration, seconds" }, { key: "avgDurationSeconds", title: "avg duration, sec" },
{ key: "timeRangeHours", title: "time range, hours" }, { key: "timeRange", sortBy: "timeRangeSeconds", title: "Query Time Interval" },
{ key: "count" } { key: "count" }
]} ]}
defaultOrderBy={"avgDurationSeconds"} defaultOrderBy={"avgDurationSeconds"}
@ -167,8 +167,8 @@ const TopQueries: FC = () => {
title={"Queries with most summary time to execute"} title={"Queries with most summary time to execute"}
columns={[ columns={[
{ key: "query" }, { key: "query" },
{ key: "sumDurationSeconds", title: "sum duration, seconds" }, { key: "sumDurationSeconds", title: "sum duration, sec" },
{ key: "timeRangeHours", title: "time range, hours" }, { key: "timeRange", sortBy: "timeRangeSeconds", title: "Query Time Interval" },
{ key: "count" } { key: "count" }
]} ]}
defaultOrderBy={"sumDurationSeconds"} defaultOrderBy={"sumDurationSeconds"}

View file

@ -51,5 +51,14 @@
&-panels { &-panels {
display: grid; display: grid;
gap: $padding-medium; gap: $padding-medium;
&__table-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: $padding-small;
height: 100%;
padding: 0 $padding-small;
}
} }
} }

View file

@ -31,6 +31,7 @@
height: 40px; height: 40px;
vertical-align: top; vertical-align: top;
line-height: 1.5; line-height: 1.5;
overflow-wrap: anywhere;
&__content { &__content {
display: flex; display: flex;
@ -68,6 +69,10 @@
&_no-wrap { &_no-wrap {
white-space: nowrap; white-space: nowrap;
} }
&_no-padding {
padding: 0;
}
} }
&__sort-icon { &__sort-icon {

View file

@ -86,7 +86,7 @@ export interface TopQuery {
query: string query: string
timeRangeSeconds: number timeRangeSeconds: number
sumDurationSeconds: number sumDurationSeconds: number
timeRangeHours: number timeRange: string
} }
export interface TopQueryStats { export interface TopQueryStats {

View file

@ -45,6 +45,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): display histograms as heatmaps in [Metrics explorer](https://docs.victoriametrics.com/#metrics-explorer). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4111). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): display histograms as heatmaps in [Metrics explorer](https://docs.victoriametrics.com/#metrics-explorer). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4111).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `WITH template` playground. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `WITH template` playground. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to debug relabeling. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3807). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to debug relabeling. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3807).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add an ability to copy and execute queries listed at [top queries](https://docs.victoriametrics.com/#top-queries) page. Also make more human readable the query duration column. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4292) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4299).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): increase default font size for better readability. * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): increase default font size for better readability.
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): [cardinality explorer](https://docs.victoriametrics.com/#cardinality-explorer): return back a table with labels containing the highest number of unique label values. See [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4213). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): [cardinality explorer](https://docs.victoriametrics.com/#cardinality-explorer): return back a table with labels containing the highest number of unique label values. See [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4213).
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add `-s3StorageClass` command-line flag for setting the storage class for AWS S3 backups. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4164). Thanks to @justcompile for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4166). * FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add `-s3StorageClass` command-line flag for setting the storage class for AWS S3 backups. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4164). Thanks to @justcompile for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4166).