Prometheus: Support utf8 metrics and labels in variable editor (#98345)

* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support utf8 metric names in explore metrics

* utf8 support in grouop by

* utf8 support in label break down

* utf8 metric and label support in metric_find_query for label values

* use the same regex to check label values

* no need to escape

* support series endpoint

* support series endpoint

* support series endpoint

* support series endpoint

* fix tests

* fix extracting labels from labelValuesQuery

* betterer
pull/98591/head
ismail simsek 4 months ago committed by GitHub
parent 019ee9c2d4
commit 4386cfc984
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 47
      packages/grafana-prometheus/src/metric_find_query.test.ts
  2. 11
      packages/grafana-prometheus/src/metric_find_query.ts
  3. 5
      packages/grafana-prometheus/src/migrations/variableMigration.ts

@ -8,6 +8,7 @@ import { PrometheusDatasource } from './datasource';
import { getPrometheusTime } from './language_utils';
import { PrometheusMetricFindQuery } from './metric_find_query';
import { PromApplication, PromOptions } from './types';
import { escapeForUtf8Support } from './utf8_support';
const fetchMock = jest.fn((options: BackendSrvRequest): Observable<FetchResponse<BackendDataSourceResponse>> => {
return of({} as unknown as FetchResponse);
@ -413,5 +414,51 @@ describe('PrometheusMetricFindQuery', () => {
});
});
// </ ModernPrometheus>
describe('utf8 metric and label support', () => {
it('utf8 label - label_values(a_utf8_http_requests_total,instance.test) should generate label values query', () => {
const metricName = 'a_utf8_http_requests_total';
const label = 'instance.test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
it('utf8 metric - label_values(utf8.http_requests_total,instance_test) should generate label values query', () => {
const metricName = 'utf8.http_requests_total';
const label = 'instance_test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${label}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
it('utf8 metric and label - label_values(utf8.http_requests_total,instance.test) should generate label values query', () => {
const metricName = 'utf8.http_requests_total';
const label = 'instance.test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
});
});
});

@ -8,9 +8,11 @@ import { getPrometheusTime } from './language_utils';
import {
PrometheusLabelNamesRegex,
PrometheusLabelNamesRegexWithMatch,
PrometheusLabelValuesRegex,
PrometheusMetricNamesRegex,
PrometheusQueryResultRegex,
} from './migrations/variableMigration';
import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
export class PrometheusMetricFindQuery {
range: TimeRange;
@ -28,7 +30,7 @@ export class PrometheusMetricFindQuery {
this.range = timeRange;
const labelNamesRegex = PrometheusLabelNamesRegex;
const labelNamesRegexWithMatch = PrometheusLabelNamesRegexWithMatch;
const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
const labelValuesRegex = PrometheusLabelValuesRegex;
const metricNamesRegex = PrometheusMetricNamesRegex;
const queryResultRegex = PrometheusQueryResultRegex;
const labelNamesQuery = this.query.match(labelNamesRegex);
@ -83,8 +85,13 @@ export class PrometheusMetricFindQuery {
const end = getPrometheusTime(this.range.to, true);
const params = { ...(metric && { 'match[]': metric }), start: start.toString(), end: end.toString() };
let escapedLabel = label;
if (!isValidLegacyName(label)) {
escapedLabel = escapeForUtf8Support(label);
}
if (!metric || this.datasource.hasLabelsMatchAPISupport()) {
const url = `/api/v1/label/${label}/values`;
const url = `/api/v1/label/${escapedLabel}/values`;
return this.datasource.metadataRequest(url, params).then((result) => {
return _map(result.data.data, (value) => {

@ -4,8 +4,7 @@ import { buildVisualQueryFromString } from '../querybuilder/parsing';
import { PromVariableQuery, PromVariableQueryType as QueryType } from '../types';
export const PrometheusLabelNamesRegex = /^label_names\(\)\s*$/;
// Note that this regex is different from the one in metric_find_query.ts because this is used pre-interpolation
export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_$][a-zA-Z0-9_]*)\)\s*$/;
export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?(.+)\)\s*$/;
export const PrometheusMetricNamesRegex = /^metrics\((.+)\)\s*$/;
export const PrometheusQueryResultRegex = /^query_result\((.+)\)\s*$/;
export const PrometheusLabelNamesRegexWithMatch = /^label_names\((.+)\)\s*$/;
@ -97,7 +96,7 @@ export function migrateVariableQueryToEditor(rawQuery: string | PromVariableQuer
return queryBase;
}
// migrate it back to a string with the correct varialbes in place
// migrate it back to a string with the correct variables in place
export function migrateVariableEditorBackToVariableSupport(QueryVariable: PromVariableQuery): string {
switch (QueryVariable.qryType) {
case QueryType.LabelNames:

Loading…
Cancel
Save