Explore Metrics: Remove overview tab in metric select view (#97118)

* introduce the exploreMetricsRemoveOverviewTab feature toggle

* handle actionView equal to overview in url

* set description in selected metric scene

* fix import

* ExploreMetrics: Disable the Overview tab by default (#98988)

feat: disable overview tab by default

* fix: remove unnecessary feature toggle

* chore: remove overview

* make i18n-extract

---------

Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>
Co-authored-by: Nick Richmond <nick.richmond@grafana.com>
This commit is contained in:
ismail simsek
2025-01-21 16:51:02 +01:00
committed by GitHub
co-authored by Nick Richmond Nick Richmond
parent 6f12b8e3a4
commit 12ae2a520c
9 changed files with 21 additions and 226 deletions
-3
View File
@@ -6153,9 +6153,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"]
],
"public/app/features/trails/ActionTabs/MetricOverviewScene.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
],
"public/app/features/trails/Breakdown/AddToFiltersGraphAction.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
],
@@ -1,191 +0,0 @@
import { useEffect } from 'react';
import { isValidLegacyName, PromMetricsMetadataItem } from '@grafana/prometheus';
import {
QueryVariable,
SceneComponentProps,
sceneGraph,
SceneObjectBase,
SceneObjectState,
VariableDependencyConfig,
VariableValueOption,
} from '@grafana/scenes';
import { Stack, Text, TextLink } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';
import { MetricScene } from '../MetricScene';
import { StatusWrapper } from '../StatusWrapper';
import { getUnitFromMetric } from '../autoQuery/units';
import { reportExploreMetrics } from '../interactions';
import { updateOtelJoinWithGroupLeft } from '../otel/util';
import { VAR_DATASOURCE_EXPR, VAR_GROUP_BY, VAR_OTEL_GROUP_LEFT } from '../shared';
import { getMetricSceneFor, getTrailFor } from '../utils';
export interface MetricOverviewSceneState extends SceneObjectState {
metadata?: PromMetricsMetadataItem;
metadataLoading?: boolean;
}
export class MetricOverviewScene extends SceneObjectBase<MetricOverviewSceneState> {
protected _variableDependency = new VariableDependencyConfig(this, {
variableNames: [VAR_DATASOURCE_EXPR],
onReferencedVariableValueChanged: this.onReferencedVariableValueChanged.bind(this),
});
constructor(state: Partial<MetricOverviewSceneState>) {
super({
...state,
});
this.addActivationHandler(this._onActivate.bind(this));
}
private getVariable(): QueryVariable {
const variable = sceneGraph.lookupVariable(VAR_GROUP_BY, this)!;
if (!(variable instanceof QueryVariable)) {
throw new Error('Group by variable not found');
}
return variable;
}
private _onActivate() {
this.updateMetadata();
}
private onReferencedVariableValueChanged() {
this.updateMetadata();
this.updateOtelGroupLeft();
}
private async updateMetadata() {
this.setState({ metadataLoading: true, metadata: undefined });
const metricScene = getMetricSceneFor(this);
const metric = metricScene.state.metric;
const trail = getTrailFor(this);
const metadata = await trail.getMetricMetadata(metric);
this.setState({ metadata, metadataLoading: false });
}
private async updateOtelGroupLeft() {
const trail = getTrailFor(this);
if (trail.state.useOtelExperience) {
await updateOtelJoinWithGroupLeft(trail, trail.state.metric ?? '');
}
}
public static Component = ({ model }: SceneComponentProps<MetricOverviewScene>) => {
const { metadata, metadataLoading } = model.useState();
const variable = model.getVariable();
const { loading: labelsLoading, options: labelOptions } = variable.useState();
let allLabelOptions = labelOptions;
const trail = getTrailFor(model);
const { useOtelExperience } = trail.useState();
if (useOtelExperience) {
// when the group left variable is changed we should get all the resource attributes + labels
const resourceAttributes = sceneGraph.lookupVariable(VAR_OTEL_GROUP_LEFT, trail)?.getValue();
if (typeof resourceAttributes === 'string') {
const attributeArray: VariableValueOption[] = resourceAttributes.split(',').map((el) => {
let label = el;
if (!isValidLegacyName(el)) {
// remove '' from label
label = el.slice(1, -1);
}
return { label, value: el };
});
allLabelOptions = attributeArray.concat(allLabelOptions);
}
}
useEffect(() => {
if (useOtelExperience) {
// this will update the group left variable
model.updateOtelGroupLeft();
}
}, [model, useOtelExperience]);
// Get unit name from the metric name
const metricScene = getMetricSceneFor(model);
const metric = metricScene.state.metric;
let unit = getUnitFromMetric(metric) ?? 'Unknown';
return (
<StatusWrapper isLoading={labelsLoading || metadataLoading}>
<Stack gap={6}>
<>
<Stack direction="column" gap={0.5}>
<Text weight={'medium'}>
<Trans i18nKey="trails.metric-overview.description-label">Description</Trans>
</Text>
<div style={{ maxWidth: 360 }}>
{metadata?.help ? (
<div>{metadata?.help}</div>
) : (
<i>
<Trans i18nKey="trails.metric-overview.no-description">No description available</Trans>
</i>
)}
</div>
</Stack>
<Stack direction="column" gap={0.5}>
<Text weight={'medium'}>
<Trans i18nKey="trails.metric-overview.type-label">Type</Trans>
</Text>
{metadata?.type ? (
<div>{metadata?.type}</div>
) : (
<i>
<Trans i18nKey="trails.metric-overview.unknown-type">Unknown</Trans>
</i>
)}
</Stack>
<Stack direction="column" gap={0.5}>
<Text weight={'medium'}>
<Trans i18nKey="trails.metric-overview.unit-label">Unit</Trans>
</Text>
{metadata?.unit ? <div>{metadata?.unit}</div> : <i>{unit}</i>}
</Stack>
<Stack direction="column" gap={0.5}>
<Text weight={'medium'}>
{useOtelExperience ? (
<Trans i18nKey="trails.metric-overview.metric-attributes">Attributes</Trans>
) : (
<Trans i18nKey="trails.metric-overview.labels">Labels</Trans>
)}
</Text>
{allLabelOptions.length === 0 && 'Unable to fetch labels.'}
{allLabelOptions.map((l) => (
<TextLink
key={l.label}
href={`#View breakdown for ${l.label}`}
title={`View breakdown for ${l.label}`}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
sceneGraph.getAncestor(model, MetricScene).setActionView('breakdown');
const groupByVar = sceneGraph.lookupVariable(VAR_GROUP_BY, model);
if (groupByVar instanceof QueryVariable && l.label != null) {
reportExploreMetrics('label_selected', { label: l.label, cause: 'overview_link' });
groupByVar.setState({ value: l.value });
}
return false;
}}
>
{l.label!}
</TextLink>
))}
</Stack>
</>
</Stack>
</StatusWrapper>
);
};
}
export function buildMetricOverviewScene() {
return new MetricOverviewScene({});
}
+4 -6
View File
@@ -16,7 +16,6 @@ import { Box, Icon, LinkButton, Stack, Tab, TabsBar, ToolbarButton, Tooltip, use
import { getExploreUrl } from '../../core/utils/explore';
import { buildMetricOverviewScene } from './ActionTabs/MetricOverviewScene';
import { buildRelatedMetricsScene } from './ActionTabs/RelatedMetricsScene';
import { buildLabelBreakdownActionScene } from './Breakdown/LabelBreakdownScene';
import { MAIN_PANEL_MAX_HEIGHT, MAIN_PANEL_MIN_HEIGHT, MetricGraphScene } from './MetricGraphScene';
@@ -39,7 +38,7 @@ import {
} from './shared';
import { getDataSource, getTrailFor, getUrlForTrail } from './utils';
const relatedLogsFeatureEnabled = config.featureToggles.exploreMetricsRelatedLogs;
const { exploreMetricsRelatedLogs } = config.featureToggles;
export interface MetricSceneState extends SceneObjectState {
body: MetricGraphScene;
@@ -69,7 +68,7 @@ export class MetricScene extends SceneObjectBase<MetricSceneState> {
private _onActivate() {
if (this.state.actionView === undefined) {
this.setActionView('overview');
this.setActionView('breakdown');
}
if (config.featureToggles.enableScopesInMetricsExplore) {
@@ -124,7 +123,6 @@ export class MetricScene extends SceneObjectBase<MetricSceneState> {
}
const actionViewsDefinitions: ActionViewDefinition[] = [
{ displayName: 'Overview', value: 'overview', getScene: buildMetricOverviewScene },
{ displayName: 'Breakdown', value: 'breakdown', getScene: buildLabelBreakdownActionScene },
{
displayName: 'Related metrics',
@@ -134,7 +132,7 @@ const actionViewsDefinitions: ActionViewDefinition[] = [
},
];
if (relatedLogsFeatureEnabled) {
if (exploreMetricsRelatedLogs) {
actionViewsDefinitions.push({
displayName: 'Related logs',
value: 'related_logs',
@@ -197,7 +195,7 @@ export class MetricActionBar extends SceneObjectBase<MetricActionBarState> {
icon="compass"
tooltip="Open in explore"
onClick={model.openExploreLink}
></ToolbarButton>
/>
<ShareTrailButton trail={trail} />
<ToolbarButton
variant={'canvas'}
@@ -1,8 +1,9 @@
import { SceneObjectState, SceneObjectBase, SceneComponentProps, VizPanel, SceneQueryRunner } from '@grafana/scenes';
import { AddToExplorationButton } from '../../MetricSelect/AddToExplorationsButton';
import { getMetricDescription } from '../../helpers/MetricDatasourceHelper';
import { MDP_METRIC_OVERVIEW, trailDS } from '../../shared';
import { getMetricSceneFor } from '../../utils';
import { getMetricSceneFor, getTrailFor } from '../../utils';
import { AutoQueryDef } from '../types';
import { AutoVizPanelQuerySelector } from './AutoVizPanelQuerySelector';
@@ -23,7 +24,12 @@ export class AutoVizPanel extends SceneObjectBase<AutoVizPanelState> {
if (!this.state.panel) {
const { autoQuery, metric } = getMetricSceneFor(this).state;
this.setState({ panel: this.getVizPanelFor(autoQuery.main, metric), metric });
this.getVizPanelFor(autoQuery.main, metric).then((panel) =>
this.setState({
panel,
metric,
})
);
}
}
@@ -32,11 +38,15 @@ export class AutoVizPanel extends SceneObjectBase<AutoVizPanelState> {
const def = metricScene.state.autoQuery.variants.find((q) => q.variant === variant)!;
this.setState({ panel: this.getVizPanelFor(def) });
this.getVizPanelFor(def).then((panel) => this.setState({ panel }));
metricScene.setState({ queryDef: def });
};
private getVizPanelFor(def: AutoQueryDef, metric?: string) {
private async getVizPanelFor(def: AutoQueryDef, metric?: string) {
const trail = getTrailFor(this);
const metadata = await trail.getMetricMetadata(metric);
const description = getMetricDescription(metadata);
return def
.vizBuilder()
.setData(
@@ -46,6 +56,7 @@ export class AutoVizPanel extends SceneObjectBase<AutoVizPanelState> {
queries: def.queries,
})
)
.setDescription(description)
.setHeaderActions([
new AutoVizPanelQuerySelector({ queryDef: def, onChangeQuery: this.onChangeQuery }),
new AddToExplorationButton({ labelName: metric ?? this.state.metric }),
@@ -13,8 +13,6 @@ type Interactions = {
cause: (
// By clicking the "select" button on that label's breakdown panel
| 'breakdown_panel'
// By clicking the label link on the overview
| 'overview_link'
// By clicking on the label selector at the top of the breakdown
| 'selector'
);
+1 -1
View File
@@ -218,7 +218,7 @@ export function limitOtelMatchTerms(
/**
* This updates the OTel join query variable that is interpolated into all queries.
* When a user is in the breakdown or overview tab, they may want to breakdown a metric by a resource attribute.
* When a user is in the breakdown tab, they may want to breakdown a metric by a resource attribute.
* The only way to do this is by enriching the metric with the target_info resource.
* This is done by joining on a unique identifier for the resource, job and instance.
* The we can get the resource attributes for the metric, enrich the metric with the join query and
+1 -1
View File
@@ -2,7 +2,7 @@ import { BusEventBase, BusEventWithPayload } from '@grafana/data';
import { ConstantVariable, SceneObject } from '@grafana/scenes';
import { VariableHide } from '@grafana/schema';
export type ActionViewType = 'overview' | 'breakdown' | 'related_logs' | 'related';
export type ActionViewType = 'breakdown' | 'related_logs' | 'related';
export interface ActionViewDefinition {
displayName: string;
-9
View File
@@ -3344,15 +3344,6 @@
"start-your-metrics-exploration": "Start your metrics exploration!",
"subtitle": "Explore your Prometheus-compatible metrics without writing a query."
},
"metric-overview": {
"description-label": "Description",
"labels": "Labels",
"metric-attributes": "Attributes",
"no-description": "No description available",
"type-label": "Type",
"unit-label": "Unit",
"unknown-type": "Unknown"
},
"metric-select": {
"filter-by": "Filter by",
"native-histogram": "Native Histogram",
@@ -3344,15 +3344,6 @@
"start-your-metrics-exploration": "Ŝŧäřŧ yőūř męŧřįčş ęχpľőřäŧįőʼn!",
"subtitle": "Ēχpľőřę yőūř Přőmęŧĥęūş-čőmpäŧįþľę męŧřįčş ŵįŧĥőūŧ ŵřįŧįʼnģ ä qūęřy."
},
"metric-overview": {
"description-label": "Đęşčřįpŧįőʼn",
"labels": "Ŀäþęľş",
"metric-attributes": "Åŧŧřįþūŧęş",
"no-description": "Ńő đęşčřįpŧįőʼn äväįľäþľę",
"type-label": "Ŧypę",
"unit-label": "Ůʼnįŧ",
"unknown-type": "Ůʼnĸʼnőŵʼn"
},
"metric-select": {
"filter-by": "Fįľŧęř þy",
"native-histogram": "Ńäŧįvę Ħįşŧőģřäm",