mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 20:37:12 +01:00
vmui: add support relative time (#2504)
* feat: add support relative time * app/vmselect: `make vmui-update` * docs/CHANGELOG.md: document the change Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
6be07e8c25
commit
9b4bff67e0
@ -1,7 +1,7 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.d8362c27.css",
|
||||
"main.js": "./static/js/main.1754e6b5.js",
|
||||
"main.js": "./static/js/main.3e17cf70.js",
|
||||
"static/js/362.1a2113d4.chunk.js": "./static/js/362.1a2113d4.chunk.js",
|
||||
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
||||
"static/media/README.md": "./static/media/README.5e5724daf3ee333540a3.md",
|
||||
@ -9,6 +9,6 @@
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.d8362c27.css",
|
||||
"static/js/main.1754e6b5.js"
|
||||
"static/js/main.3e17cf70.js"
|
||||
]
|
||||
}
|
@ -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"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><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><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.1754e6b5.js"></script><link href="./static/css/main.d8362c27.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"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><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><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.3e17cf70.js"></script><link href="./static/css/main.d8362c27.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
2
app/vmselect/vmui/static/js/main.3e17cf70.js
Normal file
2
app/vmselect/vmui/static/js/main.3e17cf70.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,46 +1,20 @@
|
||||
import React, {FC} from "preact/compat";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import dayjs from "dayjs";
|
||||
import {relativeTimeOptions} from "../../../../utils/time";
|
||||
|
||||
interface TimeDurationSelector {
|
||||
setDuration: (str: string, from: Date) => void;
|
||||
setDuration: ({duration, until, id}: {duration: string, until: Date, id: string}) => void;
|
||||
}
|
||||
|
||||
interface DurationOption {
|
||||
duration: string,
|
||||
title?: string,
|
||||
from?: () => Date,
|
||||
}
|
||||
|
||||
const durationOptions: DurationOption[] = [
|
||||
{duration: "5m", title: "Last 5 minutes"},
|
||||
{duration: "15m", title: "Last 15 minutes"},
|
||||
{duration: "30m", title: "Last 30 minutes"},
|
||||
{duration: "1h", title: "Last 1 hour"},
|
||||
{duration: "3h", title: "Last 3 hours"},
|
||||
{duration: "6h", title: "Last 6 hours"},
|
||||
{duration: "12h", title: "Last 12 hours"},
|
||||
{duration: "24h", title: "Last 24 hours"},
|
||||
{duration: "2d", title: "Last 2 days"},
|
||||
{duration: "7d", title: "Last 7 days"},
|
||||
{duration: "30d", title: "Last 30 days"},
|
||||
{duration: "90d", title: "Last 90 days"},
|
||||
{duration: "180d", title: "Last 180 days"},
|
||||
{duration: "1y", title: "Last 1 year"},
|
||||
{duration: "1d", from: () => dayjs().subtract(1, "day").endOf("day").toDate(), title: "Yesterday"},
|
||||
{duration: "1d", from: () => dayjs().endOf("day").toDate(), title: "Today"},
|
||||
];
|
||||
|
||||
const TimeDurationSelector: FC<TimeDurationSelector> = ({setDuration}) => {
|
||||
// setDurationString("5m"))
|
||||
|
||||
return <List style={{maxHeight: "168px", overflow: "auto", paddingRight: "15px"}}>
|
||||
{durationOptions.map(d =>
|
||||
<ListItem key={d.duration} button onClick={() => setDuration(d.duration, d.from ? d.from() : new Date())}>
|
||||
<ListItemText primary={d.title || d.duration}/>
|
||||
</ListItem>)}
|
||||
{relativeTimeOptions.map(({id, duration, until, title}) =>
|
||||
<ListItemButton key={id} onClick={() => setDuration({duration, until: until(), id})}>
|
||||
<ListItemText primary={title || duration}/>
|
||||
</ListItemButton>)}
|
||||
</List>;
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,7 @@ export const TimeSelector: FC = () => {
|
||||
const [until, setUntil] = useState<string>();
|
||||
const [from, setFrom] = useState<string>();
|
||||
|
||||
const {time: {period: {end, start}}} = useAppState();
|
||||
const {time: {period: {end, start}, relativeTime}} = useAppState();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
@ -52,10 +52,9 @@ export const TimeSelector: FC = () => {
|
||||
setFrom(formatDateForNativeInput(dateFromSeconds(start)));
|
||||
}, [start]);
|
||||
|
||||
const setDuration = (dur: string, from: Date) => {
|
||||
dispatch({type: "SET_UNTIL", payload: from});
|
||||
const setDuration = ({duration, until, id}: {duration: string, until: Date, id: string}) => {
|
||||
dispatch({type: "SET_RELATIVE_TIME", payload: {duration, until, id}});
|
||||
setAnchorEl(null);
|
||||
dispatch({type: "SET_DURATION", payload: dur});
|
||||
};
|
||||
|
||||
const formatRange = useMemo(() => {
|
||||
@ -80,7 +79,9 @@ export const TimeSelector: FC = () => {
|
||||
}}
|
||||
startIcon={<QueryBuilderIcon/>}
|
||||
onClick={(e) => setAnchorEl(e.currentTarget)}>
|
||||
{formatRange.start} - {formatRange.end}
|
||||
{relativeTime
|
||||
? relativeTime.replace(/_/g, " ")
|
||||
: `${formatRange.start} - ${formatRange.end}`}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Popper
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
getDateNowUTC,
|
||||
getDurationFromPeriod,
|
||||
getTimeperiodForDuration,
|
||||
getDurationFromMilliseconds
|
||||
getDurationFromMilliseconds,
|
||||
getRelativeTime
|
||||
} from "../../utils/time";
|
||||
import {getFromStorage} from "../../utils/storage";
|
||||
import {getDefaultServer} from "../../utils/default-server-url";
|
||||
@ -17,11 +18,12 @@ import dayjs from "dayjs";
|
||||
export interface TimeState {
|
||||
duration: string;
|
||||
period: TimeParams;
|
||||
relativeTime?: string;
|
||||
}
|
||||
|
||||
export interface QueryHistory {
|
||||
index: number,
|
||||
values: string[]
|
||||
index: number;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
@ -44,6 +46,7 @@ export type Action =
|
||||
| { type: "SET_QUERY_HISTORY_BY_INDEX", payload: {value: QueryHistory, queryNumber: number} }
|
||||
| { type: "SET_QUERY_HISTORY", payload: QueryHistory[] }
|
||||
| { type: "SET_DURATION", payload: string }
|
||||
| { type: "SET_RELATIVE_TIME", payload: {id: string, duration: string, until: Date} }
|
||||
| { type: "SET_UNTIL", payload: Date }
|
||||
| { type: "SET_FROM", payload: Date }
|
||||
| { type: "SET_PERIOD", payload: TimePeriod }
|
||||
@ -53,8 +56,9 @@ export type Action =
|
||||
| { type: "TOGGLE_AUTOCOMPLETE"}
|
||||
| { type: "NO_CACHE"}
|
||||
|
||||
const duration = getQueryStringValue("g0.range_input", "1h") as string;
|
||||
const endInput = formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as Date);
|
||||
const {relativeDuration, relativeUntil, relativeTimeId} = getRelativeTime();
|
||||
const duration = relativeDuration || getQueryStringValue("g0.range_input", "1h") as string;
|
||||
const endInput = relativeUntil || formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as Date);
|
||||
const query = getQueryArray();
|
||||
|
||||
export const initialState: AppState = {
|
||||
@ -64,7 +68,8 @@ export const initialState: AppState = {
|
||||
queryHistory: query.map(q => ({index: 0, values: [q]})),
|
||||
time: {
|
||||
duration,
|
||||
period: getTimeperiodForDuration(duration, new Date(endInput))
|
||||
period: getTimeperiodForDuration(duration, new Date(endInput)),
|
||||
relativeTime: relativeTimeId,
|
||||
},
|
||||
queryControls: {
|
||||
autoRefresh: false,
|
||||
@ -107,7 +112,17 @@ export function reducer(state: AppState, action: Action): AppState {
|
||||
time: {
|
||||
...state.time,
|
||||
duration: action.payload,
|
||||
period: getTimeperiodForDuration(action.payload, dateFromSeconds(state.time.period.end))
|
||||
period: getTimeperiodForDuration(action.payload, dateFromSeconds(state.time.period.end)),
|
||||
relativeTime: ""
|
||||
}
|
||||
};
|
||||
case "SET_RELATIVE_TIME":
|
||||
return {
|
||||
...state,
|
||||
time: {
|
||||
...state.time,
|
||||
period: getTimeperiodForDuration(action.payload.duration, new Date(action.payload.until)),
|
||||
relativeTime: action.payload.id,
|
||||
}
|
||||
};
|
||||
case "SET_UNTIL":
|
||||
@ -115,7 +130,8 @@ export function reducer(state: AppState, action: Action): AppState {
|
||||
...state,
|
||||
time: {
|
||||
...state.time,
|
||||
period: getTimeperiodForDuration(state.time.duration, action.payload)
|
||||
period: getTimeperiodForDuration(state.time.duration, action.payload),
|
||||
relativeTime: ""
|
||||
}
|
||||
};
|
||||
case "SET_FROM":
|
||||
@ -130,7 +146,8 @@ export function reducer(state: AppState, action: Action): AppState {
|
||||
time: {
|
||||
...state.time,
|
||||
duration: durationFrom,
|
||||
period: getTimeperiodForDuration(durationFrom, dayjs(state.time.period.end*1000).toDate())
|
||||
period: getTimeperiodForDuration(durationFrom, dayjs(state.time.period.end*1000).toDate()),
|
||||
relativeTime: ""
|
||||
}
|
||||
};
|
||||
case "SET_PERIOD":
|
||||
@ -145,7 +162,8 @@ export function reducer(state: AppState, action: Action): AppState {
|
||||
time: {
|
||||
...state.time,
|
||||
duration,
|
||||
period: getTimeperiodForDuration(duration, action.payload.to)
|
||||
period: getTimeperiodForDuration(duration, action.payload.to),
|
||||
relativeTime: ""
|
||||
}
|
||||
};
|
||||
case "TOGGLE_AUTOREFRESH":
|
||||
|
@ -54,3 +54,10 @@ export interface DashboardSettings {
|
||||
filename: string;
|
||||
rows: DashboardRow[];
|
||||
}
|
||||
|
||||
export interface RelativeTimeOption {
|
||||
id: string,
|
||||
duration: string,
|
||||
until: () => Date,
|
||||
title: string,
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ const stateToUrlParams = {
|
||||
"time.duration": "range_input",
|
||||
"time.period.date": "end_input",
|
||||
"time.period.step": "step_input",
|
||||
"time.relativeTime": "relative_time",
|
||||
"displayType": "tab"
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import {TimeParams, TimePeriod} from "../types";
|
||||
import {RelativeTimeOption, TimeParams, TimePeriod} from "../types";
|
||||
import dayjs, {UnitTypeShort} from "dayjs";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import {getQueryStringValue} from "./query-string";
|
||||
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(utc);
|
||||
@ -105,5 +106,38 @@ export const checkDurationLimit = (dur: string): string => {
|
||||
return dur;
|
||||
};
|
||||
|
||||
export const dateFromSeconds = (epochTimeInSeconds: number): Date =>
|
||||
new Date(epochTimeInSeconds * 1000);
|
||||
export const dateFromSeconds = (epochTimeInSeconds: number): Date => new Date(epochTimeInSeconds * 1000);
|
||||
|
||||
export const relativeTimeOptions: RelativeTimeOption[] = [
|
||||
{title: "Last 5 minutes", duration: "5m"},
|
||||
{title: "Last 15 minutes", duration: "15m"},
|
||||
{title: "Last 30 minutes", duration: "30m"},
|
||||
{title: "Last 1 hour", duration: "1h"},
|
||||
{title: "Last 3 hours", duration: "3h"},
|
||||
{title: "Last 6 hours", duration: "6h"},
|
||||
{title: "Last 12 hours", duration: "12h"},
|
||||
{title: "Last 24 hours", duration: "24h"},
|
||||
{title: "Last 2 days", duration: "2d"},
|
||||
{title: "Last 7 days", duration: "7d"},
|
||||
{title: "Last 30 days", duration: "30d"},
|
||||
{title: "Last 90 days", duration: "90d"},
|
||||
{title: "Last 180 days", duration: "180d"},
|
||||
{title: "Last 1 year", duration: "1y"},
|
||||
{title: "Yesterday", duration: "1d", until: () => dayjs().subtract(1, "day").endOf("day").toDate()},
|
||||
{title: "Today", duration: "1d", until: () => dayjs().endOf("day").toDate()},
|
||||
].map(o => ({
|
||||
id: o.title.replace(/\s/g, "_").toLocaleLowerCase(),
|
||||
until: o.until ? o.until : () => dayjs().toDate(),
|
||||
...o
|
||||
}));
|
||||
|
||||
export const getRelativeTime = (relativeTimeId?: string) => {
|
||||
const id = relativeTimeId || getQueryStringValue("g0.relative_time", "") as string;
|
||||
const target = relativeTimeOptions.find(d => d.id === id);
|
||||
if (!target) return {};
|
||||
return {
|
||||
relativeTimeId: id,
|
||||
relativeDuration: target.duration,
|
||||
relativeUntil: target.until()
|
||||
};
|
||||
};
|
||||
|
@ -25,6 +25,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||
* FEATURE: allow specifying TLS cipher suites for incoming https requests via `-tlsCipherSuites` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2404).
|
||||
* FEATURE: allow specifying TLS cipher suites for mTLS connections between cluster components via `-cluster.tlsCipherSuites` command-line flag. See [these docs](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#mtls-protection).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): shown an empty graph on the selected time range when there is no data on it. Previously `No data to show` placeholder was shown instead of the graph in this case. This prevented from zooming and scrolling of such a graph.
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): show the selected `last N minutes/hours/days` in the top right corner. Previously the `start - end` duration was shown instead, which could be hard to interpret. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2402).
|
||||
* FEATURE: expose `vm_indexdb_items_added_total` and `vm_indexdb_items_added_size_bytes_total` counters at `/metrics` page, which can be used for monitoring the rate for addition of new entries in `indexdb` (aka `inverted index`) alongside the total size in bytes for the added entries. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2471).
|
||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add `drop_common_labels()` function, which drops common `label="name"` pairs from the passed time series. See [these docs](https://docs.victoriametrics.com/MetricsQL.html#drop_common_labels).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user