mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 12:31:07 +01:00
vmui: query history (#1732)
* feat: add query history * fix: change detect keyUp for nav query history * feat: set default query history * app/vmselect/vmui: `make vmui-update` Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
8991c8b589
commit
2b266cb87e
@ -609,6 +609,10 @@ The UI allows exploring query results via graphs and tables. Graphs support scro
|
|||||||
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
||||||
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
||||||
|
|
||||||
|
Query history can be navigated by holding `Ctrl` (or `Cmd` on MacOS) and pressing `up` or `down` arrows on the keyboard while the cursor is located in the query input field.
|
||||||
|
|
||||||
|
When querying the [backfilled data](https://docs.victoriametrics.com/#backfilling), it may be useful disabling response cache by clicking `Enable cache` checkbox.
|
||||||
|
|
||||||
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.acc63211.chunk.css",
|
"main.css": "./static/css/main.acc63211.chunk.css",
|
||||||
"main.js": "./static/js/main.33166fff.chunk.js",
|
"main.js": "./static/js/main.fe86f8ba.chunk.js",
|
||||||
"runtime-main.js": "./static/js/runtime-main.b765a534.js",
|
"runtime-main.js": "./static/js/runtime-main.b765a534.js",
|
||||||
"static/css/2.a684aa27.chunk.css": "./static/css/2.a684aa27.chunk.css",
|
"static/css/2.a684aa27.chunk.css": "./static/css/2.a684aa27.chunk.css",
|
||||||
"static/js/2.856ca35b.chunk.js": "./static/js/2.856ca35b.chunk.js",
|
"static/js/2.632b68e4.chunk.js": "./static/js/2.632b68e4.chunk.js",
|
||||||
"static/js/3.daeccd9c.chunk.js": "./static/js/3.daeccd9c.chunk.js",
|
"static/js/3.daeccd9c.chunk.js": "./static/js/3.daeccd9c.chunk.js",
|
||||||
"index.html": "./index.html",
|
"index.html": "./index.html",
|
||||||
"static/js/2.856ca35b.chunk.js.LICENSE.txt": "./static/js/2.856ca35b.chunk.js.LICENSE.txt"
|
"static/js/2.632b68e4.chunk.js.LICENSE.txt": "./static/js/2.632b68e4.chunk.js.LICENSE.txt"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/js/runtime-main.b765a534.js",
|
"static/js/runtime-main.b765a534.js",
|
||||||
"static/css/2.a684aa27.chunk.css",
|
"static/css/2.a684aa27.chunk.css",
|
||||||
"static/js/2.856ca35b.chunk.js",
|
"static/js/2.632b68e4.chunk.js",
|
||||||
"static/css/main.acc63211.chunk.css",
|
"static/css/main.acc63211.chunk.css",
|
||||||
"static/js/main.33166fff.chunk.js"
|
"static/js/main.fe86f8ba.chunk.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"/><link href="./static/css/2.a684aa27.chunk.css" rel="stylesheet"><link href="./static/css/main.acc63211.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"daeccd9c"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.856ca35b.chunk.js"></script><script src="./static/js/main.33166fff.chunk.js"></script></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"/><link href="./static/css/2.a684aa27.chunk.css" rel="stylesheet"><link href="./static/css/main.acc63211.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"daeccd9c"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.632b68e4.chunk.js"></script><script src="./static/js/main.fe86f8ba.chunk.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/vmselect/vmui/static/js/main.fe86f8ba.chunk.js
Normal file
1
app/vmselect/vmui/static/js/main.fe86f8ba.chunk.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,17 +1,6 @@
|
|||||||
import React, {FC, useRef, useState} from "react";
|
import React, {FC, useRef, useState} from "react";
|
||||||
import {
|
import { Accordion, AccordionDetails, AccordionSummary, Box, Grid, IconButton, TextField, Typography, FormControlLabel,
|
||||||
Accordion,
|
Tooltip, Switch } from "@material-ui/core";
|
||||||
AccordionDetails,
|
|
||||||
AccordionSummary,
|
|
||||||
Box,
|
|
||||||
Grid,
|
|
||||||
IconButton,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
FormControlLabel,
|
|
||||||
Tooltip,
|
|
||||||
Switch,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import QueryEditor from "./QueryEditor";
|
import QueryEditor from "./QueryEditor";
|
||||||
import {TimeSelector} from "./TimeSelector";
|
import {TimeSelector} from "./TimeSelector";
|
||||||
import {useAppDispatch, useAppState} from "../../../state/common/StateContext";
|
import {useAppDispatch, useAppState} from "../../../state/common/StateContext";
|
||||||
@ -25,8 +14,7 @@ import {useGraphDispatch, useGraphState} from "../../../state/graph/GraphStateCo
|
|||||||
import debounce from "lodash.debounce";
|
import debounce from "lodash.debounce";
|
||||||
|
|
||||||
const QueryConfigurator: FC = () => {
|
const QueryConfigurator: FC = () => {
|
||||||
|
const {serverUrl, query, queryHistory, time: {duration}, queryControls: {autocomplete, nocache}} = useAppState();
|
||||||
const {serverUrl, query, time: {duration}, queryControls: {autocomplete, nocache}} = useAppState();
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const onChangeAutocomplete = () => {
|
const onChangeAutocomplete = () => {
|
||||||
@ -38,12 +26,10 @@ const QueryConfigurator: FC = () => {
|
|||||||
saveToStorage("NO_CACHE", !nocache);
|
saveToStorage("NO_CACHE", !nocache);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {yaxis} = useGraphState();
|
const { yaxis } = useGraphState();
|
||||||
const graphDispatch = useGraphDispatch();
|
const graphDispatch = useGraphDispatch();
|
||||||
|
|
||||||
const onChangeYaxisLimits = () => {
|
const onChangeYaxisLimits = () => { graphDispatch({type: "TOGGLE_ENABLE_YAXIS_LIMITS"}); };
|
||||||
graphDispatch({type: "TOGGLE_ENABLE_YAXIS_LIMITS"});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setMinLimit = ({target: {value}}: {target: {value: string}}) => {
|
const setMinLimit = ({target: {value}}: {target: {value: string}}) => {
|
||||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: [+value, yaxis.limits.range[1]]});
|
graphDispatch({type: "SET_YAXIS_LIMITS", payload: [+value, yaxis.limits.range[1]]});
|
||||||
@ -58,92 +44,104 @@ const QueryConfigurator: FC = () => {
|
|||||||
const queryContainer = useRef<HTMLDivElement>(null);
|
const queryContainer = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const onSetDuration = (dur: string) => dispatch({type: "SET_DURATION", payload: dur});
|
const onSetDuration = (dur: string) => dispatch({type: "SET_DURATION", payload: dur});
|
||||||
const onRunQuery = () => dispatch({type: "RUN_QUERY"});
|
|
||||||
const onSetQuery = (query: string) => dispatch({type: "SET_QUERY", payload: query});
|
const onRunQuery = () => {
|
||||||
|
const { values } = queryHistory;
|
||||||
|
if (query === values[values.length - 1]) return;
|
||||||
|
dispatch({type: "RUN_QUERY"});
|
||||||
|
dispatch({type: "SET_QUERY_HISTORY_INDEX", payload: values.length});
|
||||||
|
dispatch({type: "SET_QUERY_HISTORY_VALUES", payload: [...values, query]});
|
||||||
|
};
|
||||||
|
const onSetQuery = (newQuery: string) => {
|
||||||
|
if (query === newQuery) return;
|
||||||
|
dispatch({type: "SET_QUERY", payload: newQuery});
|
||||||
|
};
|
||||||
|
const setHistoryIndex = (step: number) => {
|
||||||
|
const index = queryHistory.index + step;
|
||||||
|
if (index < -1 || index > queryHistory.values.length) return;
|
||||||
|
dispatch({type: "SET_QUERY_HISTORY_INDEX", payload: index});
|
||||||
|
onSetQuery(queryHistory.values[index] || "");
|
||||||
|
};
|
||||||
const onSetServer = ({target: {value}}: {target: {value: string}}) => {
|
const onSetServer = ({target: {value}}: {target: {value: string}}) => {
|
||||||
dispatch({type: "SET_SERVER", payload: value});
|
dispatch({type: "SET_SERVER", payload: value});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <>
|
||||||
<>
|
<Accordion expanded={expanded} onChange={() => setExpanded(prev => !prev)}>
|
||||||
<Accordion expanded={expanded} onChange={() => setExpanded(prev => !prev)}>
|
<AccordionSummary
|
||||||
<AccordionSummary
|
expandIcon={<ExpandMoreIcon/>}
|
||||||
expandIcon={<ExpandMoreIcon/>}
|
aria-controls="panel1a-content"
|
||||||
aria-controls="panel1a-content"
|
id="panel1a-header"
|
||||||
id="panel1a-header"
|
>
|
||||||
>
|
<Box mr={2}><Typography variant="h6" component="h2">Query Configuration</Typography></Box>
|
||||||
<Box mr={2}><Typography variant="h6" component="h2">Query Configuration</Typography></Box>
|
<Box flexGrow={1} onClick={e => e.stopPropagation()} onFocusCapture={e => e.stopPropagation()}>
|
||||||
<Box flexGrow={1} onClick={e => e.stopPropagation()} onFocusCapture={e => e.stopPropagation()}>
|
<Portal disablePortal={!expanded} container={queryContainer.current}>
|
||||||
<Portal disablePortal={!expanded} container={queryContainer.current}>
|
<Box display="flex" alignItems="center">
|
||||||
<QueryEditor server={serverUrl} query={query} oneLiner={!expanded} autocomplete={autocomplete}
|
<Box width="100%">
|
||||||
runQuery={onRunQuery}
|
<QueryEditor server={serverUrl} query={query} oneLiner={!expanded} autocomplete={autocomplete}
|
||||||
setQuery={onSetQuery}/>
|
queryHistory={queryHistory} setHistoryIndex={setHistoryIndex} runQuery={onRunQuery} setQuery={onSetQuery}/>
|
||||||
</Portal>
|
|
||||||
</Box>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item xs={12} md={6}>
|
|
||||||
<Box display="grid" gridGap={16}>
|
|
||||||
<Box display="flex" alignItems="center">
|
|
||||||
<TextField variant="outlined" fullWidth label="Server URL" value={serverUrl}
|
|
||||||
inputProps={{style: {fontFamily: "Monospace"}}}
|
|
||||||
onChange={onSetServer}/>
|
|
||||||
<Box ml={1}>
|
|
||||||
<Tooltip title="Execute Query">
|
|
||||||
<IconButton onClick={onRunQuery}><PlayCircleOutlineIcon /></IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Tooltip title="Request Auth Settings">
|
|
||||||
<IconButton onClick={() => setDialogOpen(true)}><SecurityIcon/></IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box flexGrow={1} ><div ref={queryContainer} />{/* for portal QueryEditor */}</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
<Tooltip title="Execute Query">
|
||||||
<Grid item xs={8} md={6} >
|
<IconButton onClick={onRunQuery}><PlayCircleOutlineIcon /></IconButton>
|
||||||
<Box style={{
|
</Tooltip>
|
||||||
borderRadius: "4px",
|
</Box>
|
||||||
borderColor: "#b9b9b9",
|
</Portal>
|
||||||
borderStyle: "solid",
|
</Box>
|
||||||
borderWidth: "1px",
|
</AccordionSummary>
|
||||||
height: "100%",
|
<AccordionDetails>
|
||||||
}}>
|
<Grid container spacing={2}>
|
||||||
<TimeSelector setDuration={onSetDuration} duration={duration}/>
|
<Grid item xs={12} md={6}>
|
||||||
</Box>
|
<Box display="grid" gridGap={16}>
|
||||||
</Grid>
|
<Box display="flex" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<TextField variant="outlined" fullWidth label="Server URL" value={serverUrl}
|
||||||
<Box px={1} display="flex" alignItems="center" minHeight={52}>
|
inputProps={{style: {fontFamily: "Monospace"}}}
|
||||||
<Box><FormControlLabel
|
onChange={onSetServer}/>
|
||||||
control={<Switch size="small" checked={autocomplete} onChange={onChangeAutocomplete}/>}
|
<Box>
|
||||||
label="Enable autocomplete"
|
<Tooltip title="Request Auth Settings">
|
||||||
/></Box>
|
<IconButton onClick={() => setDialogOpen(true)}><SecurityIcon/></IconButton>
|
||||||
<Box ml={4}><FormControlLabel
|
</Tooltip>
|
||||||
control={<Switch size="small" checked={!nocache} onChange={onChangeCache}/>}
|
|
||||||
label="Enable cache"
|
|
||||||
/></Box>
|
|
||||||
<Box ml={4} display="flex" alignItems="center">
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch size="small" checked={yaxis.limits.enable} onChange={onChangeYaxisLimits}/>}
|
|
||||||
label="fix the limits for y-axis"
|
|
||||||
/>
|
|
||||||
{yaxis.limits.enable && <Box display="grid" gridTemplateColumns="120px 120px" gridGap={10}>
|
|
||||||
<TextField label="Min" type="number" size="small" variant="outlined"
|
|
||||||
defaultValue={yaxis.limits.range[0]} onChange={debounce(setMinLimit, 750)}/>
|
|
||||||
<TextField label="Max" type="number" size="small" variant="outlined"
|
|
||||||
defaultValue={yaxis.limits.range[1]} onChange={debounce(setMaxLimit, 750)}/>
|
|
||||||
</Box>}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
<Box flexGrow={1} ><div ref={queryContainer} />{/* for portal QueryEditor */}</Box>
|
||||||
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</AccordionDetails>
|
<Grid item xs={8} md={6} >
|
||||||
</Accordion>
|
<Box style={{
|
||||||
<AuthDialog open={dialogOpen} onClose={() => setDialogOpen(false)}/>
|
borderRadius: "4px",
|
||||||
</>
|
borderColor: "#b9b9b9",
|
||||||
);
|
borderStyle: "solid",
|
||||||
|
borderWidth: "1px",
|
||||||
|
height: "100%",}}>
|
||||||
|
<TimeSelector setDuration={onSetDuration} duration={duration}/>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Box px={1} display="flex" alignItems="center" minHeight={52}>
|
||||||
|
<Box><FormControlLabel
|
||||||
|
control={<Switch size="small" checked={autocomplete} onChange={onChangeAutocomplete}/>} label="Enable autocomplete"
|
||||||
|
/></Box>
|
||||||
|
<Box ml={4}><FormControlLabel
|
||||||
|
control={<Switch size="small" checked={!nocache} onChange={onChangeCache}/>} label="Enable cache"
|
||||||
|
/></Box>
|
||||||
|
<Box ml={4} display="flex" alignItems="center">
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Switch size="small" checked={yaxis.limits.enable} onChange={onChangeYaxisLimits}/>}
|
||||||
|
label="fix the limits for y-axis"
|
||||||
|
/>
|
||||||
|
{yaxis.limits.enable && <Box display="grid" gridTemplateColumns="120px 120px" gridGap={10}>
|
||||||
|
<TextField label="Min" type="number" size="small" variant="outlined"
|
||||||
|
defaultValue={yaxis.limits.range[0]} onChange={debounce(setMinLimit, 750)}/>
|
||||||
|
<TextField label="Max" type="number" size="small" variant="outlined"
|
||||||
|
defaultValue={yaxis.limits.range[1]} onChange={debounce(setMaxLimit, 750)}/>
|
||||||
|
</Box>}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<AuthDialog open={dialogOpen} onClose={() => setDialogOpen(false)}/>
|
||||||
|
</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueryConfigurator;
|
export default QueryConfigurator;
|
@ -4,19 +4,21 @@ import {defaultKeymap} from "@codemirror/next/commands";
|
|||||||
import React, {FC, useEffect, useRef, useState} from "react";
|
import React, {FC, useEffect, useRef, useState} from "react";
|
||||||
import { PromQLExtension } from "codemirror-promql";
|
import { PromQLExtension } from "codemirror-promql";
|
||||||
import { basicSetup } from "@codemirror/next/basic-setup";
|
import { basicSetup } from "@codemirror/next/basic-setup";
|
||||||
import {isMacOs} from "../../../utils/detect-os";
|
import {QueryHistory} from "../../../state/common/reducer";
|
||||||
|
|
||||||
export interface QueryEditorProps {
|
export interface QueryEditorProps {
|
||||||
|
setHistoryIndex: (step: number) => void;
|
||||||
setQuery: (query: string) => void;
|
setQuery: (query: string) => void;
|
||||||
runQuery: () => void;
|
runQuery: () => void;
|
||||||
query: string;
|
query: string;
|
||||||
|
queryHistory: QueryHistory;
|
||||||
server: string;
|
server: string;
|
||||||
oneLiner?: boolean;
|
oneLiner?: boolean;
|
||||||
autocomplete: boolean
|
autocomplete: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueryEditor: FC<QueryEditorProps> = ({
|
const QueryEditor: FC<QueryEditorProps> = ({
|
||||||
query, setQuery, runQuery, server, oneLiner = false, autocomplete
|
query, queryHistory, setHistoryIndex, setQuery, runQuery, server, oneLiner = false, autocomplete
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
@ -37,7 +39,6 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||||||
|
|
||||||
// update state on change of autocomplete server
|
// update state on change of autocomplete server
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
const promQL = new PromQLExtension();
|
const promQL = new PromQLExtension();
|
||||||
promQL.activateCompletion(autocomplete);
|
promQL.activateCompletion(autocomplete);
|
||||||
promQL.setComplete({url: server});
|
promQL.setComplete({url: server});
|
||||||
@ -55,24 +56,26 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||||||
keymap(defaultKeymap),
|
keymap(defaultKeymap),
|
||||||
listenerExtension,
|
listenerExtension,
|
||||||
promQL.asExtension(),
|
promQL.asExtension(),
|
||||||
keymap([
|
|
||||||
{
|
|
||||||
key: isMacOs() ? "Cmd-Enter" : "Ctrl-Enter",
|
|
||||||
run: (): boolean => {
|
|
||||||
runQuery();
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
|
}, [server, editorView, autocomplete, queryHistory]);
|
||||||
|
|
||||||
}, [server, editorView, autocomplete]);
|
const onKeyUp = (e: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
|
const {key, ctrlKey, metaKey} = e;
|
||||||
|
const ctrlMetaKey = ctrlKey || metaKey;
|
||||||
|
if (key === "Enter" && ctrlMetaKey) {
|
||||||
|
runQuery();
|
||||||
|
} else if (key === "ArrowUp" && ctrlMetaKey) {
|
||||||
|
setHistoryIndex(-1);
|
||||||
|
} else if (key === "ArrowDown" && ctrlMetaKey) {
|
||||||
|
setHistoryIndex(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/*Class one-line-scroll and other codemirror styles are declared in index.css*/}
|
{/*Class one-line-scroll and other codemirror styles are declared in index.css*/}
|
||||||
<div ref={ref} className={oneLiner ? "one-line-scroll" : undefined}/>
|
<div ref={ref} className={oneLiner ? "one-line-scroll" : undefined} onKeyUp={onKeyUp}/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint max-lines: 0 */
|
||||||
import {DisplayType} from "../../components/Home/Configurator/DisplayTypeSwitch";
|
import {DisplayType} from "../../components/Home/Configurator/DisplayTypeSwitch";
|
||||||
import {TimeParams, TimePeriod} from "../../types";
|
import {TimeParams, TimePeriod} from "../../types";
|
||||||
import {dateFromSeconds, formatDateToLocal, getDateNowUTC, getDurationFromPeriod, getTimeperiodForDuration} from "../../utils/time";
|
import {dateFromSeconds, formatDateToLocal, getDateNowUTC, getDurationFromPeriod, getTimeperiodForDuration} from "../../utils/time";
|
||||||
@ -10,11 +11,17 @@ export interface TimeState {
|
|||||||
period: TimeParams;
|
period: TimeParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QueryHistory {
|
||||||
|
index: number,
|
||||||
|
values: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
serverUrl: string;
|
serverUrl: string;
|
||||||
displayType: DisplayType;
|
displayType: DisplayType;
|
||||||
query: string;
|
query: string;
|
||||||
time: TimeState;
|
time: TimeState;
|
||||||
|
queryHistory: QueryHistory,
|
||||||
queryControls: {
|
queryControls: {
|
||||||
autoRefresh: boolean;
|
autoRefresh: boolean;
|
||||||
autocomplete: boolean,
|
autocomplete: boolean,
|
||||||
@ -26,6 +33,8 @@ export type Action =
|
|||||||
| { type: "SET_DISPLAY_TYPE", payload: DisplayType }
|
| { type: "SET_DISPLAY_TYPE", payload: DisplayType }
|
||||||
| { type: "SET_SERVER", payload: string }
|
| { type: "SET_SERVER", payload: string }
|
||||||
| { type: "SET_QUERY", payload: string }
|
| { type: "SET_QUERY", payload: string }
|
||||||
|
| { type: "SET_QUERY_HISTORY_INDEX", payload: number }
|
||||||
|
| { type: "SET_QUERY_HISTORY_VALUES", payload: string[] }
|
||||||
| { type: "SET_DURATION", payload: string }
|
| { type: "SET_DURATION", payload: string }
|
||||||
| { type: "SET_UNTIL", payload: Date }
|
| { type: "SET_UNTIL", payload: Date }
|
||||||
| { type: "SET_PERIOD", payload: TimePeriod }
|
| { type: "SET_PERIOD", payload: TimePeriod }
|
||||||
@ -37,11 +46,13 @@ export type Action =
|
|||||||
|
|
||||||
const duration = getQueryStringValue("g0.range_input", "1h") as string;
|
const duration = getQueryStringValue("g0.range_input", "1h") as string;
|
||||||
const endInput = formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as Date);
|
const endInput = formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as Date);
|
||||||
|
const query = getQueryStringValue("g0.expr", getFromStorage("LAST_QUERY") as string || "\n") as string;
|
||||||
|
|
||||||
export const initialState: AppState = {
|
export const initialState: AppState = {
|
||||||
serverUrl: getDefaultServer(),
|
serverUrl: getDefaultServer(),
|
||||||
displayType: "chart",
|
displayType: "chart",
|
||||||
query: getQueryStringValue("g0.expr", getFromStorage("LAST_QUERY") as string || "\n") as string, // demo_memory_usage_bytes
|
query: query, // demo_memory_usage_bytes
|
||||||
|
queryHistory: { index: 0, values: [query] },
|
||||||
time: {
|
time: {
|
||||||
duration,
|
duration,
|
||||||
period: getTimeperiodForDuration(duration, new Date(endInput))
|
period: getTimeperiodForDuration(duration, new Date(endInput))
|
||||||
@ -70,6 +81,22 @@ export function reducer(state: AppState, action: Action): AppState {
|
|||||||
...state,
|
...state,
|
||||||
query: action.payload
|
query: action.payload
|
||||||
};
|
};
|
||||||
|
case "SET_QUERY_HISTORY_INDEX":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
queryHistory: {
|
||||||
|
...state.queryHistory,
|
||||||
|
index: action.payload
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case "SET_QUERY_HISTORY_VALUES":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
queryHistory: {
|
||||||
|
...state.queryHistory,
|
||||||
|
values: action.payload
|
||||||
|
}
|
||||||
|
};
|
||||||
case "SET_DURATION":
|
case "SET_DURATION":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -17,7 +17,7 @@ sort: 15
|
|||||||
* FEATURE: vmalert: add `-remoteRead.disablePathAppend` command-line flag, which allows specifying the full `-remoteRead.url`. If `-remoteRead.disablePathAppend` is set, then `vmalert` doesn't add `/api/v1/query` suffix to `-remoteRead.url`.
|
* FEATURE: vmalert: add `-remoteRead.disablePathAppend` command-line flag, which allows specifying the full `-remoteRead.url`. If `-remoteRead.disablePathAppend` is set, then `vmalert` doesn't add `/api/v1/query` suffix to `-remoteRead.url`.
|
||||||
* FEATURE: add trigonometric functions, which are going to be added in [Prometheus 2.31](https://github.com/prometheus/prometheus/pull/9239): [acosh](https://docs.victoriametrics.com/MetricsQL.html#acosh), [asinh](https://docs.victoriametrics.com/MetricsQL.html#asinh), [atan](https://docs.victoriametrics.com/MetricsQL.html#atan), [atanh](https://docs.victoriametrics.com/MetricsQL.html#atanh), [cosh](https://docs.victoriametrics.com/MetricsQL.html#cosh), [deg](https://docs.victoriametrics.com/MetricsQL.html#deg), [rad](https://docs.victoriametrics.com/MetricsQL.html#rad), [sinh](https://docs.victoriametrics.com/MetricsQL.html#sinh), [tan](https://docs.victoriametrics.com/MetricsQL.html#tan), [tanh](https://docs.victoriametrics.com/MetricsQL.html#tanh). Also add `atan2` binary operator. See [this pull request](https://github.com/prometheus/prometheus/pull/9248).
|
* FEATURE: add trigonometric functions, which are going to be added in [Prometheus 2.31](https://github.com/prometheus/prometheus/pull/9239): [acosh](https://docs.victoriametrics.com/MetricsQL.html#acosh), [asinh](https://docs.victoriametrics.com/MetricsQL.html#asinh), [atan](https://docs.victoriametrics.com/MetricsQL.html#atan), [atanh](https://docs.victoriametrics.com/MetricsQL.html#atanh), [cosh](https://docs.victoriametrics.com/MetricsQL.html#cosh), [deg](https://docs.victoriametrics.com/MetricsQL.html#deg), [rad](https://docs.victoriametrics.com/MetricsQL.html#rad), [sinh](https://docs.victoriametrics.com/MetricsQL.html#sinh), [tan](https://docs.victoriametrics.com/MetricsQL.html#tan), [tanh](https://docs.victoriametrics.com/MetricsQL.html#tanh). Also add `atan2` binary operator. See [this pull request](https://github.com/prometheus/prometheus/pull/9248).
|
||||||
* FEATURE: consistently return the same set of time series from [limitk](https://docs.victoriametrics.com/MetricsQL.html#limitk) function. This improves the usability of periodically refreshed graphs.
|
* FEATURE: consistently return the same set of time series from [limitk](https://docs.victoriametrics.com/MetricsQL.html#limitk) function. This improves the usability of periodically refreshed graphs.
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): varios UX improvements. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1711).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): varios UX improvements. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1711) and [these docs](https://docs.victoriametrics.com/#vmui).
|
||||||
* FEATURE: add `/flags` page to all the VictoriaMetrics components. This page contains command-line flags passed to the component.
|
* FEATURE: add `/flags` page to all the VictoriaMetrics components. This page contains command-line flags passed to the component.
|
||||||
|
|
||||||
* BUGFIX: vmstorage: fix `unaligned 64-bit atomic operation` panic on 32-bit architectures (arm and 386). The panic has been introduced in v1.67.0.
|
* BUGFIX: vmstorage: fix `unaligned 64-bit atomic operation` panic on 32-bit architectures (arm and 386). The panic has been introduced in v1.67.0.
|
||||||
|
@ -609,6 +609,10 @@ The UI allows exploring query results via graphs and tables. Graphs support scro
|
|||||||
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
||||||
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
||||||
|
|
||||||
|
Query history can be navigated by holding `Ctrl` (or `Cmd` on MacOS) and pressing `up` or `down` arrows on the keyboard while the cursor is located in the query input field.
|
||||||
|
|
||||||
|
When querying the [backfilled data](https://docs.victoriametrics.com/#backfilling), it may be useful disabling response cache by clicking `Enable cache` checkbox.
|
||||||
|
|
||||||
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
||||||
|
|
||||||
|
|
||||||
|
@ -613,6 +613,10 @@ The UI allows exploring query results via graphs and tables. Graphs support scro
|
|||||||
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
* Drag the graph to the left / right in order to move the displayed time range into the past / future.
|
||||||
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
* Hold `Ctrl` (or `Cmd` on MacOS) and scroll up / down in order to zoom in / out the graph.
|
||||||
|
|
||||||
|
Query history can be navigated by holding `Ctrl` (or `Cmd` on MacOS) and pressing `up` or `down` arrows on the keyboard while the cursor is located in the query input field.
|
||||||
|
|
||||||
|
When querying the [backfilled data](https://docs.victoriametrics.com/#backfilling), it may be useful disabling response cache by clicking `Enable cache` checkbox.
|
||||||
|
|
||||||
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
See the [example VMUI at VictoriaMetrics playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/?g0.expr=100%20*%20sum(rate(process_cpu_seconds_total))%20by%20(job)&g0.range_input=1d).
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user