Commit 08d8190c by Peter Holmberg Committed by GitHub

PanelInspector: Add Stats Tab (#22683)

* add tab

* add process measurement

* Fixed some design issues

* Align tabs margin

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent c7557429
......@@ -434,6 +434,10 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
endTime?: number;
}
export interface DataQueryTimings {
dataProcessingTime: number;
}
export interface QueryFix {
type: string;
label: string;
......
import { ComponentClass, ComponentType } from 'react';
import { DataQueryError, DataQueryRequest } from './datasource';
import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource';
import { GrafanaPlugin, PluginMeta } from './plugin';
import { ScopedVars } from './ScopedVars';
import { LoadingState } from './data';
......@@ -19,6 +19,7 @@ export interface PanelData {
state: LoadingState;
series: DataFrame[];
request?: DataQueryRequest;
timings?: DataQueryTimings;
error?: DataQueryError;
// Contains the range from the request or a shifted time range if a request uses relative time
timeRange: TimeRange;
......
......@@ -44,7 +44,7 @@ export const InspectHeader: FC<Props> = ({
<h3>{panel.title}</h3>
<div>{formatStats(stats)}</div>
</div>
<TabsBar>
<TabsBar className={styles.tabsBar}>
{tabs.map((t, index) => {
return (
<Tab
......@@ -67,13 +67,15 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
background-color: ${headerBackground};
z-index: 1;
flex-grow: 0;
padding: ${theme.spacing.sm} ${theme.spacing.sm} 0 ${theme.spacing.lg};
`,
actions: css`
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: ${theme.spacing.md};
margin: ${theme.spacing.md};
`,
tabsBar: css`
padding-left: ${theme.spacing.md};
`,
iconWrapper: css`
cursor: pointer;
......@@ -88,6 +90,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
`,
titleWrapper: css`
margin-bottom: ${theme.spacing.lg};
padding: ${theme.spacing.sm} ${theme.spacing.sm} 0 ${theme.spacing.lg};
`,
};
});
......
......@@ -28,17 +28,18 @@ interface Props {
export enum InspectTab {
Data = 'data',
Raw = 'raw',
Request = 'request',
Issue = 'issue',
Meta = 'meta', // When result metadata exists
Error = 'error',
Stats = 'stats',
}
interface State {
// The last raw response
last: PanelData;
// Data frem the last response
// Data from the last response
data: DataFrame[];
// The selected data frame
......@@ -50,7 +51,7 @@ interface State {
// If the datasource supports custom metadata
metaDS?: DataSourceApi;
stats: { requestTime: number; queries: number; dataSources: number };
stats: { requestTime: number; queries: number; dataSources: number; processingTime: number };
drawerWidth: string;
}
......@@ -63,8 +64,8 @@ export class PanelInspector extends PureComponent<Props, State> {
data: [],
selected: 0,
tab: props.selectedTab || InspectTab.Data,
drawerWidth: '40%',
stats: { requestTime: 0, queries: 0, dataSources: 0 },
drawerWidth: '50%',
stats: { requestTime: 0, queries: 0, dataSources: 0, processingTime: 0 },
};
}
......@@ -90,6 +91,7 @@ export class PanelInspector extends PureComponent<Props, State> {
const targets = lastResult.request?.targets || [];
const requestTime = lastResult.request?.endTime ? lastResult.request?.endTime - lastResult.request.startTime : -1;
const dataSources = new Set(targets.map(t => t.datasource)).size;
const processingTime = lastResult.timings?.dataProcessingTime || -1;
// Find the first DataSource wanting to show custom metadata
if (data && targets.length) {
......@@ -123,6 +125,7 @@ export class PanelInspector extends PureComponent<Props, State> {
requestTime,
queries: targets.length,
dataSources,
processingTime,
},
}));
}
......@@ -163,11 +166,7 @@ export class PanelInspector extends PureComponent<Props, State> {
if (!metaDS || !metaDS.components?.MetadataInspector) {
return <div>No Metadata Inspector</div>;
}
return (
<CustomScrollbar>
<metaDS.components.MetadataInspector datasource={metaDS} data={data} />
</CustomScrollbar>
);
return <metaDS.components.MetadataInspector datasource={metaDS} data={data} />;
}
renderDataTab() {
......@@ -232,32 +231,44 @@ export class PanelInspector extends PureComponent<Props, State> {
);
}
renderIssueTab() {
return <CustomScrollbar>TODO: show issue form</CustomScrollbar>;
}
renderErrorTab(error?: DataQueryError) {
if (!error) {
return null;
}
if (error.data) {
return (
<CustomScrollbar>
<>
<h3>{error.data.message}</h3>
<pre>
<code>{error.data.error}</code>
</pre>
</CustomScrollbar>
<JSONFormatter json={error} open={2} />
</>
);
}
return <div>{error.message}</div>;
}
renderRawJsonTab(last: PanelData) {
renderRequestTab() {
return <JSONFormatter json={this.state.last} open={3} />;
}
renderStatsTab() {
const { stats } = this.state;
return (
<CustomScrollbar>
<JSONFormatter json={last} open={2} />
</CustomScrollbar>
<table className="filter-table width-30">
<tbody>
<tr>
<td>Query time</td>
<td>{`${stats.requestTime === -1 ? 'N/A' : stats.requestTime + 'ms'}`}</td>
</tr>
<tr>
<td>Data processing time</td>
<td>{`${
stats.processingTime === -1
? 'N/A'
: Math.round((stats.processingTime + Number.EPSILON) * 100) / 100 + 'ms'
}`}</td>
</tr>
</tbody>
</table>
);
}
......@@ -270,6 +281,9 @@ export class PanelInspector extends PureComponent<Props, State> {
tabs.push({ label: 'Data', value: InspectTab.Data });
}
tabs.push({ label: 'Stats', value: InspectTab.Stats });
tabs.push({ label: 'Request', value: InspectTab.Request });
if (this.state.metaDS) {
tabs.push({ label: 'Meta Data', value: InspectTab.Meta });
}
......@@ -278,8 +292,6 @@ export class PanelInspector extends PureComponent<Props, State> {
tabs.push({ label: 'Error', value: InspectTab.Error });
}
tabs.push({ label: 'Raw JSON', value: InspectTab.Raw });
return (
<InspectHeader
tabs={tabs}
......@@ -302,25 +314,13 @@ export class PanelInspector extends PureComponent<Props, State> {
return (
<Drawer title={this.drawerHeader} width={drawerWidth} onClose={this.onDismiss}>
<TabContent className={styles.tabContent}>
{tab === InspectTab.Data ? (
this.renderDataTab()
) : (
<AutoSizer>
{({ width, height }) => {
if (width === 0) {
return null;
}
return (
<div style={{ width, height }}>
{tab === InspectTab.Meta && this.renderMetadataInspector()}
{tab === InspectTab.Issue && this.renderIssueTab()}
{tab === InspectTab.Raw && this.renderRawJsonTab(last)}
{tab === InspectTab.Error && this.renderErrorTab(error)}
</div>
);
}}
</AutoSizer>
)}
<CustomScrollbar autoHeightMin="100%">
{tab === InspectTab.Data && this.renderDataTab()}
{tab === InspectTab.Meta && this.renderMetadataInspector()}
{tab === InspectTab.Request && this.renderRequestTab()}
{tab === InspectTab.Error && this.renderErrorTab(error)}
{tab === InspectTab.Stats && this.renderStatsTab()}
</CustomScrollbar>
</TabContent>
</Drawer>
);
......
......@@ -216,8 +216,13 @@ export function preProcessPanelData(data: PanelData, lastResult: PanelData): Pan
}
// Make sure the data frames are properly formatted
const STARTTIME = performance.now();
const processedDataFrames = getProcessedDataFrames(series);
const STOPTIME = performance.now();
return {
...data,
series: getProcessedDataFrames(series),
series: processedDataFrames,
timings: { dataProcessingTime: STOPTIME - STARTTIME },
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment