From 4262ad92cec1485f354bd8a924b702ef917b385c Mon Sep 17 00:00:00 2001 From: CSTDev Date: Wed, 23 Oct 2019 11:47:37 +0100 Subject: [PATCH] React ui add query stats (#6190) * Adds the query stats to UI Adds the query load time, resolution and total number of time series, as the current UI has Signed-off-by: cstdev * Implement unit test for QueryStats Signed-off-by: cstdev * Tidy Query Stats component Rename it and expose a interface for the values it displays Make it a functional component as it has no state or lifecycle Better null/undefined checks Only render if needed, decided by the panel Remove old stats if the next errors Signed-off-by: cstdev --- web/ui/react-app/src/Panel.tsx | 30 ++++++++++++++++++++----- web/ui/react-app/src/QueryStatsView.css | 5 +++++ web/ui/react-app/src/QueryStatsView.tsx | 20 +++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 web/ui/react-app/src/QueryStatsView.css create mode 100644 web/ui/react-app/src/QueryStatsView.tsx diff --git a/web/ui/react-app/src/Panel.tsx b/web/ui/react-app/src/Panel.tsx index b89f0db17..6450eb1ee 100644 --- a/web/ui/react-app/src/Panel.tsx +++ b/web/ui/react-app/src/Panel.tsx @@ -19,6 +19,7 @@ import GraphControls from './GraphControls'; import Graph from './Graph'; import DataTable from './DataTable'; import TimeInput from './TimeInput'; +import QueryStatsView, { QueryStats } from './QueryStatsView'; interface PanelProps { options: PanelOptions; @@ -36,7 +37,7 @@ interface PanelState { } | null; loading: boolean; error: string | null; - stats: null; // TODO: Stats. + stats: QueryStats | null, } export interface PanelOptions { @@ -100,6 +101,7 @@ class Panel extends Component { } executeQuery = (expr: string): void => { + const queryStart = Date.now(); if (this.props.options.expr !== expr) { this.setOptions({expr: expr}); } @@ -119,7 +121,6 @@ class Panel extends Component { const endTime = this.getEndTime().valueOf() / 1000; // TODO: shouldn't valueof only work when it's a moment? const startTime = endTime - this.props.options.range; const resolution = this.props.options.resolution || Math.max(Math.floor(this.props.options.range / 250), 1); - const url = new URL(window.location.href); const params: {[key: string]: string} = { 'query': expr, @@ -153,14 +154,29 @@ class Panel extends Component { throw new Error(json.error || 'invalid response JSON'); } + let resultSeries = 0; + if (json.data) { + const { resultType, result } = json.data; + if (resultType === "scalar") { + resultSeries = 1; + } else if (result && result.length > 0) { + resultSeries = result.length; + } + } + this.setState({ error: null, data: json.data, lastQueryParams: { - startTime: startTime, - endTime: endTime, - resolution: resolution, + startTime, + endTime, + resolution, }, + stats: { + loadTime: Date.now() - queryStart, + resolution, + resultSeries + }, loading: false, }); this.abortInFlightFetch = null; @@ -246,6 +262,10 @@ class Panel extends Component { Graph + { + (!this.state.loading && !this.state.error && this.state.stats) && + + } diff --git a/web/ui/react-app/src/QueryStatsView.css b/web/ui/react-app/src/QueryStatsView.css new file mode 100644 index 000000000..d5d8dc7c9 --- /dev/null +++ b/web/ui/react-app/src/QueryStatsView.css @@ -0,0 +1,5 @@ +.query-stats{ + flex-grow: 1; + font-size: 9px; + color: #999; +} diff --git a/web/ui/react-app/src/QueryStatsView.tsx b/web/ui/react-app/src/QueryStatsView.tsx new file mode 100644 index 000000000..a7cca4da8 --- /dev/null +++ b/web/ui/react-app/src/QueryStatsView.tsx @@ -0,0 +1,20 @@ +import React, { FC } from 'react'; +import './QueryStatsView.css'; + +export interface QueryStats { + loadTime: number; + resolution: number; + resultSeries: number; +} + +const QueryStatsView: FC = props => { + const {loadTime, resolution, resultSeries} = props; + + return ( +
+ Load time: {loadTime}ms   Resolution: {resolution}s   Result series: {resultSeries} +
+ ); +} + +export default QueryStatsView;