vmui: reduced the number of server requests (#5253)

* vmui: reduced the number of server requests

* run `make vmui-update vmui-logs-update`

---------

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
Yury Molodov 2023-11-14 01:50:00 +01:00 committed by Aliaksandr Valialkin
parent d6a2264709
commit 0fe02e8d9d
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
26 changed files with 502 additions and 344 deletions

View file

@ -1,13 +1,13 @@
{
"files": {
"main.css": "./static/css/main.9a224445.css",
"main.js": "./static/js/main.02178f4b.js",
"static/js/522.b5ae4365.chunk.js": "./static/js/522.b5ae4365.chunk.js",
"static/media/MetricsQL.md": "./static/media/MetricsQL.957b90ab4cb4852eec26.md",
"main.css": "./static/css/main.d1313636.css",
"main.js": "./static/js/main.1919fefe.js",
"static/js/522.da77e7b3.chunk.js": "./static/js/522.da77e7b3.chunk.js",
"static/media/MetricsQL.md": "./static/media/MetricsQL.8644fd7c964802dd34a9.md",
"index.html": "./index.html"
},
"entrypoints": [
"static/css/main.9a224445.css",
"static/js/main.02178f4b.js"
"static/css/main.d1313636.css",
"static/js/main.1919fefe.js"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.02178f4b.js"></script><link href="./static/css/main.9a224445.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.1919fefe.js"></script><link href="./static/css/main.d1313636.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,11 @@
---
sort: 14
weight: 14
sort: 23
weight: 23
title: MetricsQL
menu:
docs:
parent: "victoriametrics"
weight: 14
parent: 'victoriametrics'
weight: 23
aliases:
- /ExtendedPromQL.html
- /MetricsQL.html
@ -21,7 +21,8 @@ However, there are some [intentional differences](https://medium.com/@romanhavro
[Standalone MetricsQL package](https://godoc.org/github.com/VictoriaMetrics/metricsql) can be used for parsing MetricsQL in external apps.
If you are unfamiliar with PromQL, then it is suggested reading [this tutorial for beginners](https://medium.com/@valyala/promql-tutorial-for-beginners-9ab455142085).
If you are unfamiliar with PromQL, then it is suggested reading [this tutorial for beginners](https://medium.com/@valyala/promql-tutorial-for-beginners-9ab455142085)
and introduction into [basic querying via MetricsQL](https://docs.victoriametrics.com/keyConcepts.html#metricsql).
The following functionality is implemented differently in MetricsQL compared to PromQL. This improves user experience:
@ -109,7 +110,7 @@ The list of MetricsQL features on top of PromQL:
* [histogram_quantile](#histogram_quantile) accepts optional third arg - `boundsLabel`.
In this case it returns `lower` and `upper` bounds for the estimated percentile.
See [this issue for details](https://github.com/prometheus/prometheus/issues/5706).
* `default` binary operator. `q1 default q2` fills gaps in `q1` with the corresponding values from `q2`.
* `default` binary operator. `q1 default q2` fills gaps in `q1` with the corresponding values from `q2`. See also [drop_empty_series](#drop_empty_series).
* `if` binary operator. `q1 if q2` removes values from `q1` for missing values from `q2`.
* `ifnot` binary operator. `q1 ifnot q2` removes values from `q1` for existing values from `q2`.
* `WITH` templates. This feature simplifies writing and managing complex queries.
@ -531,7 +532,7 @@ See also [duration_over_time](#duration_over_time) and [lag](#lag).
`mad_over_time(series_selector[d])` is a [rollup function](#rollup-functions), which calculates [median absolute deviation](https://en.wikipedia.org/wiki/Median_absolute_deviation)
over raw samples on the given lookbehind window `d` per each time series returned from the given [series_selector](https://docs.victoriametrics.com/keyConcepts.html#filtering).
See also [mad](#mad) and [range_mad](#range_mad).
See also [mad](#mad), [range_mad](#range_mad) and [outlier_iqr_over_time](#outlier_iqr_over_time).
#### max_over_time
@ -561,6 +562,18 @@ This function is supported by PromQL. See also [tmin_over_time](#tmin_over_time)
for raw samples on the given lookbehind window `d`. It is calculated individually per each time series returned
from the given [series_selector](https://docs.victoriametrics.com/keyConcepts.html#filtering). It is expected that raw sample values are discrete.
#### outlier_iqr_over_time
`outlier_iqr_over_time(series_selector[d])` is a [rollup function](#rollup-functions), which returns the last sample on the given lookbehind window `d`
if its value is either smaller than the `q25-1.5*iqr` or bigger than `q75+1.5*iqr` where:
- `iqr` is an [Interquartile range](https://en.wikipedia.org/wiki/Interquartile_range) over raw samples on the lookbehind window `d`
- `q25` and `q75` are 25th and 75th [percentiles](https://en.wikipedia.org/wiki/Percentile) over raw samples on the lookbehind window `d`.
The `outlier_iqr_over_time()` is useful for detecting anomalies in gauge values based on the previous history of values.
For example, `outlier_iqr_over_time(memory_usage_bytes[1h])` triggers when `memory_usage_bytes` suddenly goes outside the usual value range for the last 24 hours.
See also [outliers_iqr](#outliers_iqr).
#### predict_linear
`predict_linear(series_selector[d], t)` is a [rollup function](#rollup-functions), which calculates the value `t` seconds in the future using
@ -865,7 +878,7 @@ from the given [series_selector](https://docs.victoriametrics.com/keyConcepts.ht
Metric names are stripped from the resulting rollups. Add [keep_metric_names](#keep_metric_names) modifier in order to keep metric names.
See also [zscore](#zscore) and [range_trim_zscore](#range_trim_zscore).
See also [zscore](#zscore), [range_trim_zscore](#range_trim_zscore) and [outlier_iqr_over_time](#outlier_iqr_over_time).
### Transform functions
@ -1055,6 +1068,17 @@ Metric names are stripped from the resulting series. Add [keep_metric_names](#ke
This function is supported by PromQL. See also [rad](#rad).
#### drop_empty_series
`drop_empty_series(q)` is a [transform function](#transform-functions), which drops empty series from `q`.
This function can be used when `default` operator should be applied only to non-empty series. For example,
`drop_empty_series(temperature < 30) default 42` returns series, which have at least a single sample smaller than 30 on the selected time range,
while filling gaps in the returned series with 42.
On the other hand `(temperature < 30) default 40` returns all the `temperature` series, even if they have no samples smaller than 30,
by replacing all the values bigger or equal to 30 with 40.
#### end
`end()` is a [transform function](#transform-functions), which returns the unix timestamp in seconds for the last point.
@ -1591,7 +1615,7 @@ which maps `label` values from `src_*` to `dst*` for all the time series returne
which drops time series from `q` with `label` not matching the given `regexp`.
This function can be useful after [rollup](#rollup)-like functions, which may return multiple time series for every input series.
See also [label_mismatch](#label_mismatch).
See also [label_mismatch](#label_mismatch) and [labels_equal](#labels_equal).
#### label_mismatch
@ -1599,7 +1623,7 @@ See also [label_mismatch](#label_mismatch).
which drops time series from `q` with `label` matching the given `regexp`.
This function can be useful after [rollup](#rollup)-like functions, which may return multiple time series for every input series.
See also [label_match](#label_match).
See also [label_match](#label_match) and [labels_equal](#labels_equal).
#### label_move
@ -1642,23 +1666,30 @@ for the given `label` for every time series returned by `q`.
For example, if `label_value(foo, "bar")` is applied to `foo{bar="1.234"}`, then it will return a time series
`foo{bar="1.234"}` with `1.234` value. Function will return no data for non-numeric label values.
#### labels_equal
`labels_equal(q, "label1", "label2", ...)` is [label manipulation function](#label-manipulation-functions), which returns `q` series with identical values for the listed labels
"label1", "label2", etc.
See also [label_match](#label_match) and [label_mismatch](#label_mismatch).
#### sort_by_label
`sort_by_label(q, label1, ... labelN)` is [label manipulation function](#label-manipulation-functions), which sorts series in ascending order by the given set of labels.
`sort_by_label(q, "label1", ... "labelN")` is [label manipulation function](#label-manipulation-functions), which sorts series in ascending order by the given set of labels.
For example, `sort_by_label(foo, "bar")` would sort `foo` series by values of the label `bar` in these series.
See also [sort_by_label_desc](#sort_by_label_desc) and [sort_by_label_numeric](#sort_by_label_numeric).
#### sort_by_label_desc
`sort_by_label_desc(q, label1, ... labelN)` is [label manipulation function](#label-manipulation-functions), which sorts series in descending order by the given set of labels.
`sort_by_label_desc(q, "label1", ... "labelN")` is [label manipulation function](#label-manipulation-functions), which sorts series in descending order by the given set of labels.
For example, `sort_by_label(foo, "bar")` would sort `foo` series by values of the label `bar` in these series.
See also [sort_by_label](#sort_by_label) and [sort_by_label_numeric_desc](#sort_by_label_numeric_desc).
#### sort_by_label_numeric
`sort_by_label_numeric(q, label1, ... labelN)` is [label manipulation function](#label-manipulation-functions), which sorts series in ascending order by the given set of labels
`sort_by_label_numeric(q, "label1", ... "labelN")` is [label manipulation function](#label-manipulation-functions), which sorts series in ascending order by the given set of labels
using [numeric sort](https://www.gnu.org/software/coreutils/manual/html_node/Version-sort-is-not-the-same-as-numeric-sort.html).
For example, if `foo` series have `bar` label with values `1`, `101`, `15` and `2`, then `sort_by_label_numeric(foo, "bar")` would return series
in the following order of `bar` label values: `1`, `2`, `15` and `101`.
@ -1667,7 +1698,7 @@ See also [sort_by_label_numeric_desc](#sort_by_label_numeric_desc) and [sort_by_
#### sort_by_label_numeric_desc
`sort_by_label_numeric_desc(q, label1, ... labelN)` is [label manipulation function](#label-manipulation-functions), which sorts series in descending order
`sort_by_label_numeric_desc(q, "label1", ... "labelN")` is [label manipulation function](#label-manipulation-functions), which sorts series in descending order
by the given set of labels using [numeric sort](https://www.gnu.org/software/coreutils/manual/html_node/Version-sort-is-not-the-same-as-numeric-sort.html).
For example, if `foo` series have `bar` label with values `1`, `101`, `15` and `2`, then `sort_by_label_numeric(foo, "bar")`
would return series in the following order of `bar` label values: `101`, `15`, `2` and `1`.
@ -1839,20 +1870,33 @@ This function is supported by PromQL.
`mode(q) by (group_labels)` is [aggregate function](#aggregate-functions), which returns [mode](https://en.wikipedia.org/wiki/Mode_(statistics))
per each `group_labels` for all the time series returned by `q`. The aggregate is calculated individually per each group of points with the same timestamp.
#### outliers_iqr
`outliers_iqr(q)` is [aggregate function](#aggregate-functions), which returns time series from `q` with at least a single point
outside e.g. [Interquartile range outlier bounds](https://en.wikipedia.org/wiki/Interquartile_range) `[q25-1.5*iqr .. q75+1.5*iqr]`
comparing to other time series at the given point, where:
- `iqr` is an [Interquartile range](https://en.wikipedia.org/wiki/Interquartile_range) calculated independently per each point on the graph across `q` series.
- `q25` and `q75` are 25th and 75th [percentiles](https://en.wikipedia.org/wiki/Percentile) calculated independently per each point on the graph across `q` series.
The `outliers_iqr()` is useful for detecting anomalous series in the group of series. For example, `outliers_iqr(temperature) by (country)` returns
per-country series with anomalous outlier values comparing to the rest of per-country series.
See also [outliers_mad](#outliers_mad), [outliersk](#outliersk) and [outlier_iqr_over_time](#outlier_iqr_over_time).
#### outliers_mad
`outliers_mad(tolerance, q)` is [aggregate function](#aggregate-functions), which returns time series from `q` with at least
a single point outside [Median absolute deviation](https://en.wikipedia.org/wiki/Median_absolute_deviation) (aka MAD) multiplied by `tolerance`.
E.g. it returns time series with at least a single point below `median(q) - mad(q)` or a single point above `median(q) + mad(q)`.
See also [outliersk](#outliersk) and [mad](#mad).
See also [outliers_iqr](#outliers_iqr), [outliersk](#outliersk) and [mad](#mad).
#### outliersk
`outliersk(k, q)` is [aggregate function](#aggregate-functions), which returns up to `k` time series with the biggest standard deviation (aka outliers)
out of time series returned by `q`.
See also [outliers_mad](#outliers_mad).
See also [outliers_iqr](#outliers_iqr) and [outliers_mad](#outliers_mad).
#### quantile
@ -1972,7 +2016,7 @@ See also [bottomk_min](#bottomk_min).
per each `group_labels` for all the time series returned by `q`. The aggregate is calculated individually per each group of points with the same timestamp.
This function is useful for detecting anomalies in the group of related time series.
See also [zscore_over_time](#zscore_over_time) and [range_trim_zscore](#range_trim_zscore).
See also [zscore_over_time](#zscore_over_time), [range_trim_zscore](#range_trim_zscore) and [outliers_iqr](#outliers_iqr).
## Subqueries

View file

@ -1,13 +1,13 @@
{
"files": {
"main.css": "./static/css/main.b863450b.css",
"main.js": "./static/js/main.5566464c.js",
"main.css": "./static/css/main.349e6522.css",
"main.js": "./static/js/main.c93073e5.js",
"static/js/522.da77e7b3.chunk.js": "./static/js/522.da77e7b3.chunk.js",
"static/media/MetricsQL.md": "./static/media/MetricsQL.8644fd7c964802dd34a9.md",
"index.html": "./index.html"
},
"entrypoints": [
"static/css/main.b863450b.css",
"static/js/main.5566464c.js"
"static/css/main.349e6522.css",
"static/js/main.c93073e5.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.5566464c.js"></script><link href="./static/css/main.b863450b.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.c93073e5.js"></script><link href="./static/css/main.349e6522.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,7 +7,7 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @remix-run/router v1.7.2
* @remix-run/router v1.10.0
*
* Copyright (c) Remix Software Inc.
*
@ -18,7 +18,7 @@
*/
/**
* React Router DOM v6.14.2
* React Router DOM v6.17.0
*
* Copyright (c) Remix Software Inc.
*
@ -29,7 +29,7 @@
*/
/**
* React Router v6.14.2
* React Router v6.17.0
*
* Copyright (c) Remix Software Inc.
*

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,9 @@ import { TuneIcon } from "../../Main/Icons";
import Button from "../../Main/Button/Button";
import classNames from "classnames";
import useBoolean from "../../../hooks/useBoolean";
import useEventListener from "../../../hooks/useEventListener";
import Tooltip from "../../Main/Tooltip/Tooltip";
import { AUTOCOMPLETE_KEY } from "../../Main/ShortcutKeys/constants/keyList";
const AdditionalSettingsControls: FC<{isMobile?: boolean}> = ({ isMobile }) => {
const { autocomplete } = useQueryState();
@ -29,6 +32,16 @@ const AdditionalSettingsControls: FC<{isMobile?: boolean}> = ({ isMobile }) => {
queryDispatch({ type: "TOGGLE_AUTOCOMPLETE" });
};
const handleKeyDown = (e: KeyboardEvent) => {
const { key, ctrlKey, metaKey, shiftKey } = e;
if (key === "a" && shiftKey && (ctrlKey || metaKey)) {
e.preventDefault();
onChangeAutocomplete();
}
};
useEventListener("keydown", handleKeyDown);
return (
<div
className={classNames({
@ -36,12 +49,14 @@ const AdditionalSettingsControls: FC<{isMobile?: boolean}> = ({ isMobile }) => {
"vm-additional-settings_mobile": isMobile
})}
>
<Switch
label={"Autocomplete"}
value={autocomplete}
onChange={onChangeAutocomplete}
fullWidth={isMobile}
/>
<Tooltip title={AUTOCOMPLETE_KEY}>
<Switch
label={"Autocomplete"}
value={autocomplete}
onChange={onChangeAutocomplete}
fullWidth={isMobile}
/>
</Tooltip>
<Switch
label={"Disable cache"}
value={nocache}

View file

@ -12,7 +12,8 @@
}
&__inputs {
display: flex;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
@ -21,9 +22,5 @@
&_mobile {
gap: $padding-small;
}
div {
flex-grow: 1;
}
}
}

View file

@ -5,6 +5,8 @@ import GraphTips from "../../../Chart/GraphTips/GraphTips";
const ctrlMeta = <code>{isMacOs() ? "Cmd" : "Ctrl"}</code>;
export const AUTOCOMPLETE_KEY = <>{ctrlMeta} + <code>Shift</code> + <code>A</code></>;
const keyList = [
{
title: "Query",
@ -28,6 +30,10 @@ const keyList = [
{
keys: <>{ctrlMeta} + <code>click</code> by <VisibilityIcon/></>,
description: "Toggle multiple queries"
},
{
keys: AUTOCOMPLETE_KEY,
description: "Toggle autocomplete"
}
]
},

View file

@ -9,11 +9,13 @@
&_textarea:after {
content: attr(data-replicated-value) " ";
white-space: pre-wrap;
word-wrap: break-word;
visibility: hidden;
}
&__input,
&::after {
font-family: $font-family-monospace;
width: 100%;
padding: $padding-small $padding-global;
border: $border-divider;
@ -22,6 +24,7 @@
line-height: 18px;
grid-area: 1 / 1 / 2 / 2;
overflow: hidden;
box-sizing: border-box;
}
&__label,
@ -84,7 +87,6 @@
}
&__input {
font-family: $font-family-monospace;
display: block;
border-radius: $border-radius-small;
transition: border 200ms ease;

View file

@ -1,8 +1,12 @@
import React, { useEffect, useState } from "preact/compat";
import React, { useEffect, useState, useRef } from "preact/compat";
import { StateUpdater } from "preact/hooks";
import { useAppState } from "../state/common/StateContext";
import { AutocompleteOptions } from "../components/Main/Autocomplete/Autocomplete";
import { LabelIcon, MetricIcon, ValueIcon } from "../components/Main/Icons";
import { useTimeState } from "../state/time/TimeStateContext";
import { useCallback } from "react";
import qs from "qs";
import dayjs from "dayjs";
enum TypeData {
metric,
@ -11,9 +15,10 @@ enum TypeData {
}
type FetchDataArgs = {
url: string;
urlSuffix: string;
setter: StateUpdater<AutocompleteOptions[]>;
type: TypeData;
params?: URLSearchParams;
}
const icons = {
@ -22,16 +27,39 @@ const icons = {
[TypeData.value]: <ValueIcon />,
};
const QUERY_LIMIT = 1000;
export const useFetchQueryOptions = ({ metric, label }: { metric: string; label: string }) => {
const { serverUrl } = useAppState();
const { period: { start, end } } = useTimeState();
const [metrics, setMetrics] = useState<AutocompleteOptions[]>([]);
const [labels, setLabels] = useState<AutocompleteOptions[]>([]);
const [values, setValues] = useState<AutocompleteOptions[]>([]);
const fetchData = async ({ url, setter, type, }: FetchDataArgs) => {
const prevParams = useRef<Record<string, URLSearchParams>>({});
const getQueryParams = useCallback((params?: Record<string, string>) => {
const roundedStart = dayjs(start).startOf("day").valueOf();
const roundedEnd = dayjs(end).endOf("day").valueOf();
return new URLSearchParams({
...(params || {}),
limit: `${QUERY_LIMIT}`,
start: `${roundedStart}`,
end: `${roundedEnd}`
});
}, [start, end]);
const isParamsEqual = (prev: URLSearchParams, next: URLSearchParams) => {
const queryNext = qs.parse(next.toString());
const queryPrev = qs.parse(prev.toString());
return JSON.stringify(queryPrev) === JSON.stringify(queryNext);
};
const fetchData = async ({ urlSuffix, setter, type, params }: FetchDataArgs) => {
try {
const response = await fetch(url);
const response = await fetch(`${serverUrl}/api/v1/${urlSuffix}?${params}`);
if (response.ok) {
const { data } = await response.json() as { data: string[] };
setter(data.map(l => ({
@ -51,12 +79,19 @@ export const useFetchQueryOptions = ({ metric, label }: { metric: string; label:
return;
}
const params = getQueryParams();
const prev = prevParams.current.metrics || new URLSearchParams({});
if (isParamsEqual(params, prev)) return;
fetchData({
url: `${serverUrl}/api/v1/label/__name__/values`,
urlSuffix: "label/__name__/values",
setter: setMetrics,
type: TypeData.metric
type: TypeData.metric,
params
});
}, [serverUrl]);
prevParams.current = { ...prevParams.current, metrics: params };
}, [serverUrl, getQueryParams]);
useEffect(() => {
const notFoundMetric = !metrics.find(m => m.value === metric);
@ -65,12 +100,19 @@ export const useFetchQueryOptions = ({ metric, label }: { metric: string; label:
return;
}
const params = getQueryParams({ "match[]": metric });
const prev = prevParams.current.labels || new URLSearchParams({});
if (isParamsEqual(params, prev)) return;
fetchData({
url: `${serverUrl}/api/v1/labels?match[]=${metric}`,
urlSuffix: "labels",
setter: setLabels,
type: TypeData.label
type: TypeData.label,
params
});
}, [serverUrl, metric]);
prevParams.current = { ...prevParams.current, labels: params };
}, [serverUrl, metric, getQueryParams]);
useEffect(() => {
const notFoundMetric = !metrics.find(m => m.value === metric);
@ -80,12 +122,19 @@ export const useFetchQueryOptions = ({ metric, label }: { metric: string; label:
return;
}
const params = getQueryParams({ "match[]": metric });
const prev = prevParams.current.values || new URLSearchParams({});
if (isParamsEqual(params, prev)) return;
fetchData({
url: `${serverUrl}/api/v1/label/${label}/values?match[]=${metric}`,
urlSuffix: `label/${label}/values`,
setter: setValues,
type: TypeData.value
type: TypeData.value,
params
});
}, [serverUrl, metric, label]);
prevParams.current = { ...prevParams.current, values: params };
}, [serverUrl, metric, label, getQueryParams]);
return {
metrics,

View file

@ -1,12 +1,9 @@
import React, { FC } from "preact/compat";
import dayjs from "dayjs";
import "./style.scss";
import React, { FC, memo } from "preact/compat";
import { CodeIcon, IssueIcon, LogoShortIcon, WikiIcon } from "../../components/Main/Icons";
import useDeviceDetect from "../../hooks/useDeviceDetect";
import "./style.scss";
const Footer: FC = () => {
const { isMobile } = useDeviceDetect();
const copyrightYears = `2019-${dayjs().format("YYYY")}`;
const Footer: FC = memo(() => {
const copyrightYears = `2019-${new Date().getFullYear()}`;
return <footer className="vm-footer">
<a
@ -34,7 +31,7 @@ const Footer: FC = () => {
rel="help noreferrer"
>
<WikiIcon/>
{isMobile ? "Docs" : "Documentation"}
Documentation
</a>
<a
className="vm-link vm-footer__link"
@ -43,12 +40,12 @@ const Footer: FC = () => {
rel="noreferrer"
>
<IssueIcon/>
{isMobile ? "New issue" : "Create an issue"}
Create an issue
</a>
<div className="vm-footer__copyright">
&copy; {copyrightYears} VictoriaMetrics
</div>
</footer>;
};
});
export default Footer;

View file

@ -8,6 +8,7 @@ import { useSearchParams } from "react-router-dom";
import dayjs from "dayjs";
import { DATE_FORMAT } from "../../../constants/date";
import { getTenantIdFromUrl } from "../../../utils/tenants";
import usePrevious from "../../../hooks/usePrevious";
export const useFetchQuery = (): {
fetchUrl?: string[],
@ -23,6 +24,7 @@ export const useFetchQuery = (): {
const focusLabel = searchParams.get("focusLabel");
const topN = +(searchParams.get("topN") || 10);
const date = searchParams.get("date") || dayjs().tz().format(DATE_FORMAT);
const prevDate = usePrevious(date);
const { serverUrl } = useAppState();
const [isLoading, setIsLoading] = useState(false);
@ -76,11 +78,14 @@ export const useFetchQuery = (): {
const urls = [
getCardinalityInfo(serverUrl, requestParams),
getCardinalityInfo(serverUrl, prevDayParams),
getCardinalityInfo(serverUrl, totalParams),
];
if (prevDate !== date) {
urls.push(getCardinalityInfo(serverUrl, totalParams));
}
try {
const [resp, respPrev, respTotals] = await Promise.all(urls.map(getResponseJson));
const [resp, respPrev, respTotals = {}] = await Promise.all(urls.map(getResponseJson));
const prevResult = { ...respPrev.data };
const { data: dataTotal } = respTotals;
@ -90,7 +95,7 @@ export const useFetchQuery = (): {
totalLabelValuePairs: resp.data?.totalLabelValuePairs || resp.data?.headStats?.numLabelValuePairs || 0,
seriesCountByLabelName: resp.data?.seriesCountByLabelName || [],
seriesCountByFocusLabelValue: resp.data?.seriesCountByFocusLabelValue || [],
totalSeriesByAll: dataTotal?.totalSeries || dataTotal?.headStats?.numSeries || 0,
totalSeriesByAll: dataTotal?.totalSeries || dataTotal?.headStats?.numSeries || tsdbStatus.totalSeriesByAll || 0,
totalSeriesPrev: prevResult?.totalSeries || prevResult?.headStats?.numSeries || 0,
};

View file

@ -112,6 +112,7 @@ const QueryHistory: FC<Props> = ({ handleSelectQuery }) => {
variant="text"
onClick={handleOpenModal}
startIcon={<ClockIcon/>}
ariaLabel={"Show history"}
/>
</Tooltip>