diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx index 58efb0e28f0..df14c9e02c8 100644 --- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx +++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx @@ -54,6 +54,49 @@ describe('buildSelector()', () => { ]; expect(buildSelector(labels)).toEqual('foo{bar="baz"}'); }); + + describe('utf8 support', () => { + it('metric selector with utf8 metric', () => { + const labels: SelectableLabel[] = [ + { name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] }, + ]; + expect(buildSelector(labels)).toEqual('{"utf8.metric"}'); + }); + + it('metric selector with utf8 labels', () => { + const labels: SelectableLabel[] = [ + { name: '__name__', selected: true, values: [{ name: 'foo', selected: true }] }, + { name: 'utf8.label', selected: true, values: [{ name: 'baz', selected: true }] }, + ]; + expect(buildSelector(labels)).toEqual('foo{"utf8.label"="baz"}'); + }); + + it('metric selector with utf8 labels and metrics', () => { + const labels: SelectableLabel[] = [ + { name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] }, + { name: 'utf8.label', selected: true, values: [{ name: 'baz', selected: true }] }, + ]; + expect(buildSelector(labels)).toEqual('{"utf8.metric","utf8.label"="baz"}'); + }); + + it('metric selector with utf8 metric and with utf8/non-utf8 labels', () => { + const labels: SelectableLabel[] = [ + { name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] }, + { name: 'utf8.label', selected: true, values: [{ name: 'uuu', selected: true }] }, + { name: 'bar', selected: true, values: [{ name: 'baz', selected: true }] }, + ]; + expect(buildSelector(labels)).toEqual('{"utf8.metric","utf8.label"="uuu",bar="baz"}'); + }); + + it('metric selector with non-utf8 metric with utf8/non-utf8 labels', () => { + const labels: SelectableLabel[] = [ + { name: '__name__', selected: true, values: [{ name: 'foo', selected: true }] }, + { name: 'utf8.label', selected: true, values: [{ name: 'uuu', selected: true }] }, + { name: 'bar', selected: true, values: [{ name: 'baz', selected: true }] }, + ]; + expect(buildSelector(labels)).toEqual('foo{"utf8.label"="uuu",bar="baz"}'); + }); + }); }); describe('facetLabels()', () => { diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx index 963590f3667..1eeb59d35ed 100644 --- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx +++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx @@ -19,6 +19,7 @@ import { import PromQlLanguageProvider from '../language_provider'; import { escapeLabelValueInExactSelector, escapeLabelValueInRegexSelector } from '../language_utils'; +import { isValidLegacyName, utf8Support } from '../utf8_support'; // Hard limit on labels to render const EMPTY_SELECTOR = '{}'; @@ -68,22 +69,38 @@ export interface SelectableLabel { export function buildSelector(labels: SelectableLabel[]): string { let singleMetric = ''; - const selectedLabels = []; + const selectedLabels: string[] = []; for (const label of labels) { if ((label.name === METRIC_LABEL || label.selected) && label.values && label.values.length > 0) { const selectedValues = label.values.filter((value) => value.selected).map((value) => value.name); if (selectedValues.length > 1) { - selectedLabels.push(`${label.name}=~"${selectedValues.map(escapeLabelValueInRegexSelector).join('|')}"`); + selectedLabels.push( + `${utf8Support(label.name)}=~"${selectedValues.map(escapeLabelValueInRegexSelector).join('|')}"` + ); } else if (selectedValues.length === 1) { if (label.name === METRIC_LABEL) { singleMetric = selectedValues[0]; } else { - selectedLabels.push(`${label.name}="${escapeLabelValueInExactSelector(selectedValues[0])}"`); + selectedLabels.push(`${utf8Support(label.name)}="${escapeLabelValueInExactSelector(selectedValues[0])}"`); } } } } - return [singleMetric, '{', selectedLabels.join(','), '}'].join(''); + + const selectorParts: string[] = []; + const isLegacyName = singleMetric === '' || isValidLegacyName(singleMetric); + + if (isLegacyName) { + selectorParts.push(singleMetric, '{'); + } else { + selectorParts.push('{', `"${singleMetric}"`); + if (selectedLabels.length > 0) { + selectorParts.push(','); + } + } + + selectorParts.push(selectedLabels.join(','), '}'); + return selectorParts.join(''); } export function facetLabels(