mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-02-09 15:27:11 +00:00
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:
parent
9855b38da2
commit
1e4a9a8dfe
8 changed files with 65 additions and 11 deletions
|
@ -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) => ({
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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).
|
||||||
|
|
Loading…
Reference in a new issue