mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
parent
816202bca7
commit
c71c2b37c5
13 changed files with 77 additions and 13 deletions
2
app/vmui/packages/vmui/package-lock.json
generated
2
app/vmui/packages/vmui/package-lock.json
generated
|
@ -30,7 +30,7 @@
|
|||
"sass": "^1.56.0",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"typescript": "~4.6.2",
|
||||
"uplot": "^1.6.19",
|
||||
"uplot": "^1.6.30",
|
||||
"web-vitals": "^3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
"sass": "^1.56.0",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"typescript": "~4.6.2",
|
||||
"uplot": "^1.6.19",
|
||||
"uplot": "^1.6.30",
|
||||
"web-vitals": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface LineChartProps {
|
|||
layoutSize: ElementSize;
|
||||
height?: number;
|
||||
anomalyView?: boolean;
|
||||
spanGaps?: boolean;
|
||||
}
|
||||
|
||||
const LineChart: FC<LineChartProps> = ({
|
||||
|
@ -53,7 +54,8 @@ const LineChart: FC<LineChartProps> = ({
|
|||
setPeriod,
|
||||
layoutSize,
|
||||
height,
|
||||
anomalyView
|
||||
anomalyView,
|
||||
spanGaps = false
|
||||
}) => {
|
||||
const { isDarkTheme } = useAppState();
|
||||
|
||||
|
@ -106,10 +108,10 @@ const LineChart: FC<LineChartProps> = ({
|
|||
useEffect(() => {
|
||||
if (!uPlotInst) return;
|
||||
delSeries(uPlotInst);
|
||||
addSeries(uPlotInst, series);
|
||||
addSeries(uPlotInst, series, spanGaps);
|
||||
setBand(uPlotInst, series);
|
||||
uPlotInst.redraw();
|
||||
}, [series]);
|
||||
}, [series, spanGaps]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!uPlotInst) return;
|
||||
|
|
|
@ -7,16 +7,21 @@ import Popper from "../../Main/Popper/Popper";
|
|||
import "./style.scss";
|
||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||
import useBoolean from "../../../hooks/useBoolean";
|
||||
import LinesConfigurator from "./LinesConfigurator/LinesConfigurator";
|
||||
|
||||
const title = "Axes settings";
|
||||
const title = "Graph settings";
|
||||
|
||||
interface GraphSettingsProps {
|
||||
yaxis: YaxisState,
|
||||
setYaxisLimits: (limits: AxisRange) => void,
|
||||
toggleEnableLimits: () => void
|
||||
toggleEnableLimits: () => void,
|
||||
spanGaps: {
|
||||
value: boolean,
|
||||
onChange: (value: boolean) => void,
|
||||
},
|
||||
}
|
||||
|
||||
const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEnableLimits }) => {
|
||||
const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEnableLimits, spanGaps }) => {
|
||||
const popperRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -55,6 +60,10 @@ const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEn
|
|||
setYaxisLimits={setYaxisLimits}
|
||||
toggleEnableLimits={toggleEnableLimits}
|
||||
/>
|
||||
<LinesConfigurator
|
||||
spanGaps={spanGaps.value}
|
||||
onChange={spanGaps.onChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Popper>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import React, { FC } from "preact/compat";
|
||||
import Switch from "../../../Main/Switch/Switch";
|
||||
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
||||
|
||||
interface Props {
|
||||
spanGaps: boolean,
|
||||
onChange: (value: boolean) => void,
|
||||
}
|
||||
|
||||
const LinesConfigurator: FC<Props> = ({ spanGaps, onChange }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
|
||||
return <div>
|
||||
<Switch
|
||||
value={spanGaps}
|
||||
onChange={onChange}
|
||||
label="Connect null values"
|
||||
fullWidth={isMobile}
|
||||
/>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default LinesConfigurator;
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
&__body {
|
||||
display: grid;
|
||||
gap: $padding-small;
|
||||
gap: $padding-large;
|
||||
padding: 0 $padding-global;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface GraphViewProps {
|
|||
height?: number;
|
||||
isHistogram?: boolean;
|
||||
anomalyView?: boolean;
|
||||
spanGaps?: boolean;
|
||||
}
|
||||
|
||||
const GraphView: FC<GraphViewProps> = ({
|
||||
|
@ -58,6 +59,7 @@ const GraphView: FC<GraphViewProps> = ({
|
|||
height,
|
||||
isHistogram,
|
||||
anomalyView,
|
||||
spanGaps
|
||||
}) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const { timezone } = useTimeState();
|
||||
|
@ -196,6 +198,7 @@ const GraphView: FC<GraphViewProps> = ({
|
|||
layoutSize={containerSize}
|
||||
height={height}
|
||||
anomalyView={anomalyView}
|
||||
spanGaps={spanGaps}
|
||||
/>
|
||||
)}
|
||||
{isHistogram && (
|
||||
|
|
|
@ -20,7 +20,7 @@ type Props = {
|
|||
const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
|
||||
const { customStep, yaxis } = useGraphState();
|
||||
const { customStep, yaxis, spanGaps } = useGraphState();
|
||||
const { period } = useTimeState();
|
||||
const { query } = useQueryState();
|
||||
|
||||
|
@ -35,6 +35,10 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
|
|||
graphDispatch({ type: "TOGGLE_ENABLE_YAXIS_LIMITS" });
|
||||
};
|
||||
|
||||
const setSpanGaps = (value: boolean) => {
|
||||
graphDispatch({ type: "SET_SPAN_GAPS", payload: value });
|
||||
};
|
||||
|
||||
const setPeriod = ({ from, to }: {from: Date, to: Date}) => {
|
||||
timeDispatch({ type: "SET_PERIOD", payload: { from, to } });
|
||||
};
|
||||
|
@ -46,6 +50,7 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
|
|||
yaxis={yaxis}
|
||||
setYaxisLimits={setYaxisLimits}
|
||||
toggleEnableLimits={toggleEnableLimits}
|
||||
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -64,6 +69,7 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
|
|||
height={isMobile ? window.innerHeight * 0.5 : 500}
|
||||
isHistogram={isHistogram}
|
||||
anomalyView={anomalyView}
|
||||
spanGaps={spanGaps}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -34,6 +34,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
|||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [spanGaps, setSpanGaps] = useState(false);
|
||||
const [yaxis, setYaxis] = useState<YaxisState>({
|
||||
limits: {
|
||||
enable: false,
|
||||
|
@ -121,6 +122,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
|||
yaxis={yaxis}
|
||||
setYaxisLimits={setYaxisLimits}
|
||||
toggleEnableLimits={toggleEnableLimits}
|
||||
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-predefined-panel-body">
|
||||
|
@ -140,6 +142,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
|||
setPeriod={setPeriod}
|
||||
fullWidth={false}
|
||||
height={isMobile ? window.innerHeight * 0.5 : 500}
|
||||
spanGaps={spanGaps}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
|
|||
}, [data]);
|
||||
const [displayType, setDisplayType] = useState(tabs[0].value);
|
||||
|
||||
const { yaxis } = useGraphState();
|
||||
const { yaxis, spanGaps } = useGraphState();
|
||||
const graphDispatch = useGraphDispatch();
|
||||
|
||||
const setYaxisLimits = (limits: AxisRange) => {
|
||||
|
@ -60,6 +60,10 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
|
|||
graphDispatch({ type: "TOGGLE_ENABLE_YAXIS_LIMITS" });
|
||||
};
|
||||
|
||||
const setSpanGaps = (value: boolean) => {
|
||||
graphDispatch({ type: "SET_SPAN_GAPS", payload: value });
|
||||
};
|
||||
|
||||
const handleChangeDisplayType = (newValue: string) => {
|
||||
setDisplayType(newValue as DisplayType);
|
||||
};
|
||||
|
@ -137,6 +141,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
|
|||
yaxis={yaxis}
|
||||
setYaxisLimits={setYaxisLimits}
|
||||
toggleEnableLimits={toggleEnableLimits}
|
||||
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
|
||||
/>
|
||||
)}
|
||||
{displayType === "table" && (
|
||||
|
@ -161,6 +166,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
|
|||
setPeriod={() => null}
|
||||
height={isMobile ? window.innerHeight * 0.5 : 500}
|
||||
isHistogram={isHistogram}
|
||||
spanGaps={spanGaps}
|
||||
/>
|
||||
)}
|
||||
{liveData && (displayType === "code") && (
|
||||
|
|
|
@ -15,6 +15,8 @@ export interface GraphState {
|
|||
customStep: string
|
||||
yaxis: YaxisState
|
||||
isHistogram: boolean
|
||||
/** when true, null data values will not cause line breaks */
|
||||
spanGaps: boolean
|
||||
}
|
||||
|
||||
export type GraphAction =
|
||||
|
@ -22,13 +24,15 @@ export type GraphAction =
|
|||
| { type: "SET_YAXIS_LIMITS", payload: AxisRange }
|
||||
| { type: "SET_CUSTOM_STEP", payload: string}
|
||||
| { type: "SET_IS_HISTOGRAM", payload: boolean }
|
||||
| { type: "SET_SPAN_GAPS", payload: boolean }
|
||||
|
||||
export const initialGraphState: GraphState = {
|
||||
customStep: getQueryStringValue("g0.step_input", "") as string,
|
||||
yaxis: {
|
||||
limits: { enable: false, range: { "1": [0, 0] } }
|
||||
},
|
||||
isHistogram: false
|
||||
isHistogram: false,
|
||||
spanGaps: false,
|
||||
};
|
||||
|
||||
export function reducer(state: GraphState, action: GraphAction): GraphState {
|
||||
|
@ -65,6 +69,11 @@ export function reducer(state: GraphState, action: GraphAction): GraphState {
|
|||
...state,
|
||||
isHistogram: action.payload
|
||||
};
|
||||
case "SET_SPAN_GAPS":
|
||||
return {
|
||||
...state,
|
||||
spanGaps: action.payload
|
||||
};
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ export const getSeriesItemContext = (data: MetricResult[], hideSeries: string[],
|
|||
width,
|
||||
stroke,
|
||||
points,
|
||||
spanGaps: false,
|
||||
forecast: forecast.value,
|
||||
forecastGroup: forecast.group,
|
||||
freeFormFields: d.metric,
|
||||
|
@ -165,8 +166,9 @@ export const delSeries = (u: uPlot) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const addSeries = (u: uPlot, series: uPlotSeries[]) => {
|
||||
export const addSeries = (u: uPlot, series: uPlotSeries[], spanGaps = false) => {
|
||||
series.forEach((s) => {
|
||||
if (s.label) s.spanGaps = spanGaps;
|
||||
u.addSeries(s);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ See also [LTS releases](https://docs.victoriametrics.com/LTS-releases.html).
|
|||
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add support for `enable_compression` option in [scrape_configs](https://docs.victoriametrics.com/sd_configs/#scrape_configs) in order to be compatible with Prometheus scrape configs. See [this pull request](https://github.com/prometheus/prometheus/pull/13166) and [this feature request](https://github.com/prometheus/prometheus/issues/12319). Note that `vmagent` was always supporting [`disable_compression` option](https://docs.victoriametrics.com/vmagent/#scrape_config-enhancements) before Prometheus added `enable_compression` option.
|
||||
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): support client-side TLS configuration for [InfluxDB](https://docs.victoriametrics.com/vmctl/#migrating-data-from-influxdb-1x), [Remote Read protocol](https://docs.victoriametrics.com/vmctl/#migrating-data-by-remote-read-protocol) and [OpenTSDB](https://docs.victoriametrics.com/vmctl/#migrating-data-from-opentsdb). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5748). Thanks to @khushijain21 for pull requests [1](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5783), [2](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5798), [3](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5797).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): preserve [`WITH` templates](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/expand-with-exprs) when clicking the `prettify query` button at the right side of query input field. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5383).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add a feature that allows to choose how zero values, representing gaps in data, are displayed on a chart: time series data points with gaps are either connected or show breaks. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5152).
|
||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/#vmalert): support filtering by group, rule or labels in [vmalert's UI](https://docs.victoriametrics.com/vmalert/#web) for `/groups` and `/alerts` pages. See [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5791) by @victoramsantos.
|
||||
* FEATURE: [docker-compose](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#docker-compose-environment-for-victoriametrics): create a separate [docker-compose environment](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/docker-compose-victorialogs.yml) for VictoriaLogs installation, including fluentbit and [VictoriaLogs Grafana datasource](https://github.com/VictoriaMetrics/victorialogs-datasource).
|
||||
* FEATURE: [vmbackupmanager](https://docs.victoriametrics.com/vmbackupmanager/): wait for up 30 seconds before making a [snapshot](https://docs.victoriametrics.com/#how-to-work-with-snapshots) for backup if `vmstorage` is temporarily unavailalbe. This should prevent from `vmbackupmanager` termination in this case. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5859).
|
||||
|
|
Loading…
Reference in a new issue