datatrails: feat: metadata for metric panels (#84872)

* feat: metadata for metric panels

* fix: missing import?

* fix: async await to get description first

- also add description to metric-name-only panels

* fix: change description, remove from main viz

* fix: remove import
This commit is contained in:
Darren Janeczek
2024-03-22 10:05:20 -04:00
committed by GitHub
parent a2795f1291
commit 08c24e4133
5 changed files with 113 additions and 23 deletions
@@ -10,9 +10,7 @@ import {
} from '@grafana/scenes';
import { Stack, Text, TextLink } from '@grafana/ui';
import PrometheusLanguageProvider from '../../../plugins/datasource/prometheus/language_provider';
import { PromMetricsMetadataItem } from '../../../plugins/datasource/prometheus/types';
import { getDatasourceSrv } from '../../plugins/datasource_srv';
import { ALL_VARIABLE_VALUE } from '../../variables/constants';
import { StatusWrapper } from '../StatusWrapper';
import { TRAILS_ROUTE, VAR_DATASOURCE_EXPR, VAR_GROUP_BY } from '../shared';
@@ -58,25 +56,12 @@ export class MetricOverviewScene extends SceneObjectBase<MetricOverviewSceneStat
private async updateMetadata() {
this.setState({ metadataLoading: true, metadata: undefined });
const ds = await getDatasourceSrv().get(VAR_DATASOURCE_EXPR, { __sceneObject: { value: this } });
const languageProvider: PrometheusLanguageProvider = ds.languageProvider;
if (!languageProvider) {
return;
}
const metricScene = getMetricSceneFor(this);
const metric = metricScene.state.metric;
if (languageProvider.metricsMetadata) {
this.setState({ metadata: languageProvider.metricsMetadata[metric], metadataLoading: false });
return;
}
await languageProvider.start();
this.setState({ metadata: languageProvider.metricsMetadata?.[metric], metadataLoading: false });
const trail = getTrailFor(this);
const metadata = await trail.getMetricMetadata(metric);
this.setState({ metadata, metadataLoading: false });
}
public static Component = ({ model }: SceneComponentProps<MetricOverviewScene>) => {
@@ -8,12 +8,13 @@ import { getColorByIndex } from '../utils';
import { getAutoQueriesForMetric } from './AutoQueryEngine';
export function getPreviewPanelFor(metric: string, index: number, currentFilterCount: number) {
export function getPreviewPanelFor(metric: string, index: number, currentFilterCount: number, description?: string) {
const autoQuery = getAutoQueriesForMetric(metric);
const vizPanel = autoQuery.preview
.vizBuilder()
.setColor({ mode: 'fixed', fixedColor: getColorByIndex(index) })
.setDescription(description)
.setHeaderActions(new SelectMetricAction({ metric, title: 'Select' }))
.build();
+23
View File
@@ -18,7 +18,9 @@ import {
SceneRefreshPicker,
SceneTimePicker,
SceneTimeRange,
SceneVariable,
SceneVariableSet,
VariableDependencyConfig,
VariableValueSelectors,
} from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui';
@@ -29,6 +31,7 @@ import { MetricScene } from './MetricScene';
import { MetricSelectScene } from './MetricSelectScene';
import { MetricsHeader } from './MetricsHeader';
import { getTrailStore } from './TrailStore/TrailStore';
import { MetricDatasourceHelper } from './helpers/MetricDatasourceHelper';
import { MetricSelectedEvent, trailDS, VAR_DATASOURCE, VAR_FILTERS } from './shared';
import { getUrlForTrail } from './utils';
@@ -111,6 +114,26 @@ export class DataTrail extends SceneObjectBase<DataTrailState> {
};
}
protected _variableDependency = new VariableDependencyConfig(this, {
variableNames: [VAR_DATASOURCE],
onReferencedVariableValueChanged: async (variable: SceneVariable) => {
const { name } = variable.state;
if (name === VAR_DATASOURCE) {
this.datasourceHelper.reset();
}
},
});
private datasourceHelper = new MetricDatasourceHelper(this);
public getMetricMetadata(metric?: string) {
return this.datasourceHelper.getMetricMetadata(metric);
}
public getCurrentMetricMetadata() {
return this.getMetricMetadata(this.state.metric);
}
private goBackToStep(step: DataTrailHistoryStep) {
if (!this.state.embedded) {
getUrlSyncManager().cleanUp(this);
@@ -26,6 +26,7 @@ import { getPreviewPanelFor } from './AutomaticMetricQueries/previewPanel';
import { MetricScene } from './MetricScene';
import { SelectMetricAction } from './SelectMetricAction';
import { StatusWrapper } from './StatusWrapper';
import { getMetricDescription } from './helpers/MetricDatasourceHelper';
import { sortRelatedMetrics } from './relatedMetrics';
import { getVariablesWithMetricConstant, trailDS, VAR_DATASOURCE, VAR_FILTERS_EXPR, VAR_METRIC_NAMES } from './shared';
import { getFilters, getTrailFor } from './utils';
@@ -202,7 +203,7 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> {
this.previewCache = metricsMap;
}
private buildLayout() {
private async buildLayout() {
// Temp hack when going back to select metric scene and variable updates
if (this.ignoreNextUpdate) {
this.ignoreNextUpdate = false;
@@ -225,6 +226,8 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> {
const children: SceneFlexItem[] = [];
const trail = getTrailFor(this);
const metricsList = this.sortedPreviewMetrics();
// Get the current filters to determine the count of them
@@ -234,13 +237,16 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> {
for (let index = 0; index < metricsList.length; index++) {
const metric = metricsList[index];
const metadata = await trail.getMetricMetadata(metric.name);
const description = getMetricDescription(metadata);
if (this.state.showPreviews) {
if (metric.itemRef && metric.isPanel) {
children.push(metric.itemRef.resolve());
continue;
}
const panel = getPreviewPanelFor(metric.name, index, currentFilterCount);
const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description);
metric.itemRef = panel.getRef();
metric.isPanel = true;
children.push(panel);
@@ -249,7 +255,7 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> {
$variables: new SceneVariableSet({
variables: getVariablesWithMetricConstant(metric.name),
}),
body: getCardPanelFor(metric.name),
body: getCardPanelFor(metric.name, description),
});
metric.itemRef = panel.getRef();
metric.isPanel = false;
@@ -357,9 +363,10 @@ function getMetricNamesVariableSet() {
});
}
function getCardPanelFor(metric: string) {
function getCardPanelFor(metric: string, description?: string) {
return PanelBuilders.text()
.setTitle(metric)
.setDescription(description)
.setHeaderActions(new SelectMetricAction({ metric, title: 'Select' }))
.setOption('content', '')
.build();
@@ -0,0 +1,74 @@
import { DataSourceApi } from '@grafana/data';
import { PromMetricsMetadata, PromMetricsMetadataItem } from '@grafana/prometheus';
import { getDataSourceSrv } from '@grafana/runtime';
import PrometheusLanguageProvider from '../../../plugins/datasource/prometheus/language_provider';
import { DataTrail } from '../DataTrail';
import { VAR_DATASOURCE_EXPR } from '../shared';
export class MetricDatasourceHelper {
constructor(trail: DataTrail) {
this._trail = trail;
}
public reset() {
this._datasource = undefined;
this._metricsMetadata = undefined;
}
private _trail: DataTrail;
private _datasource?: Promise<DataSourceApi>;
private async getDatasource() {
if (!this._datasource) {
this._datasource = getDataSourceSrv().get(VAR_DATASOURCE_EXPR, { __sceneObject: { value: this._trail } });
}
const ds = await this._datasource;
return ds;
}
private _metricsMetadata?: Promise<PromMetricsMetadata | undefined>;
private async _getMetricsMetadata() {
const ds = await this.getDatasource();
if (ds.languageProvider instanceof PrometheusLanguageProvider) {
if (!ds.languageProvider.metricsMetadata) {
await ds.languageProvider.start();
}
return ds.languageProvider.metricsMetadata!;
}
return undefined;
}
public async getMetricMetadata(metric?: string) {
if (!metric) {
return undefined;
}
if (!this._metricsMetadata) {
this._metricsMetadata = this._getMetricsMetadata();
}
const metadata = await this._metricsMetadata;
return metadata?.[metric];
}
}
export function getMetricDescription(metadata?: PromMetricsMetadataItem) {
if (!metadata) {
return undefined;
}
const { type, help, unit } = metadata;
const lines = [
help, //
type && `**Type:** *${type}*`,
unit && `**Unit:** ${unit}`,
];
return lines.join('\n\n');
}