From 0b5d1a0e51118b95f402e3d9f31d7d308f742d02 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Thu, 26 Jun 2025 02:16:07 +0200 Subject: [PATCH] Prometheus: Fix adding metric name to matcher (#107214) check metric name before pushing --- .../grafana-prometheus/src/datasource.test.ts | 25 ++++++--- packages/grafana-prometheus/src/datasource.ts | 52 +++++++++---------- .../src/language_provider.test.ts | 11 ++++ .../src/language_provider.ts | 4 +- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index 225acd63163..a9c184781db 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -16,7 +16,7 @@ import { } from '@grafana/data'; import { config, getBackendSrv, setBackendSrv, TemplateSrv } from '@grafana/runtime'; -import { extractRuleMappingFromGroups, PrometheusDatasource } from './datasource'; +import { extractResourceMatcher, extractRuleMappingFromGroups, PrometheusDatasource } from './datasource'; import { prometheusRegularEscape, prometheusSpecialRegexEscape } from './escaping'; import { PrometheusLanguageProviderInterface } from './language_provider'; import { @@ -983,7 +983,7 @@ describe('PrometheusDatasource', () => { }, ]; - const result = ds.extractResourceMatcher(queries, filters); + const result = extractResourceMatcher(queries, filters); expect(result).toBe('{__name__=~"metric_name",instance="localhost"}'); }); @@ -996,7 +996,7 @@ describe('PrometheusDatasource', () => { ]; const filters: AdHocVariableFilter[] = []; - const result = ds.extractResourceMatcher(queries, filters); + const result = extractResourceMatcher(queries, filters); expect(result).toBe('{__name__=~"metric_name"}'); }); @@ -1015,7 +1015,7 @@ describe('PrometheusDatasource', () => { }, ]; - const result = ds.extractResourceMatcher(queries, filters); + const result = extractResourceMatcher(queries, filters); expect(result).toBe('{__name__!="",instance="localhost"}'); }); @@ -1034,7 +1034,7 @@ describe('PrometheusDatasource', () => { }, ]; - const result = ds.extractResourceMatcher(queries, filters); + const result = extractResourceMatcher(queries, filters); expect(result).toBe('{__name__!="",instance="localhost",job!="testjob"}'); }); @@ -1042,9 +1042,22 @@ describe('PrometheusDatasource', () => { const queries: PromQuery[] = []; const filters: AdHocVariableFilter[] = []; - const result = ds.extractResourceMatcher(queries, filters); + const result = extractResourceMatcher(queries, filters); expect(result).toBe('{__name__!=""}'); }); + + it('should extract the correct matcher for queries with `... or vector(0)`', () => { + const queries: PromQuery[] = [ + { + refId: 'A', + expr: `sum(increase(go_cpu_classes_idle_cpu_seconds_total[$__rate_interval])) or vector(0)`, + }, + ]; + const filters: AdHocVariableFilter[] = []; + + const result = extractResourceMatcher(queries, filters); + expect(result).toBe('{__name__=~"go_cpu_classes_idle_cpu_seconds_total"}'); + }); }); }); diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 032039cc00a..f2e70b358cc 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -526,7 +526,7 @@ export class PrometheusDatasource .map((k) => ({ value: k, text: k })); } - const match = this.extractResourceMatcher(options.queries ?? [], options.filters); + const match = extractResourceMatcher(options.queries ?? [], options.filters); let labelKeys: string[] = await this.languageProvider.queryLabelKeys(options.timeRange, match); @@ -557,7 +557,7 @@ export class PrometheusDatasource ).map((v) => ({ value: v, text: v })); } - const match = this.extractResourceMatcher(options.queries ?? [], options.filters); + const match = extractResourceMatcher(options.queries ?? [], options.filters); return (await this.languageProvider.queryLabelValues(options.timeRange, options.key, match)).map((v) => ({ value: v, @@ -565,30 +565,6 @@ export class PrometheusDatasource })); } - /** - * It creates a matcher string for resource calls - * @param queries - * @param adhocFilters - * - * @example - * queries=[{expr:`metricName{label="value"}`}] - * adhocFilters={key:"instance", operator:"=", value:"localhost"} - * returns {__name__=~"metricName", instance="localhost"} - */ - extractResourceMatcher(queries: PromQuery[], adhocFilters: AdHocVariableFilter[]): string { - // Extract metric names from queries we have already - const metricMatch = populateMatchParamsFromQueries(queries); - const labelFilters: QueryBuilderLabelFilter[] = adhocFilters.map((f) => ({ - label: f.key, - value: f.value, - op: f.operator, - })); - // Extract label filters from the filters we have already - const labelsMatch = renderLabelsWithoutBrackets(labelFilters); - // Create a matcher using metric names and label filters - return `{${[metricMatch, ...labelsMatch].join(',')}}`; - } - interpolateVariablesInQueries( queries: PromQuery[], scopedVars: ScopedVars, @@ -911,3 +887,27 @@ export function extractRuleMappingFromGroups(groups: RawRecordingRules[]): RuleQ {} ); } + +/** + * It creates a matcher string for resource calls + * @param queries + * @param adhocFilters + * + * @example + * queries=[{expr:`metricName{label="value"}`}] + * adhocFilters={key:"instance", operator:"=", value:"localhost"} + * returns {__name__=~"metricName", instance="localhost"} + */ +export const extractResourceMatcher = (queries: PromQuery[], adhocFilters: AdHocVariableFilter[]): string => { + // Extract metric names from queries we have already + const metricMatch = populateMatchParamsFromQueries(queries); + const labelFilters: QueryBuilderLabelFilter[] = adhocFilters.map((f) => ({ + label: f.key, + value: f.value, + op: f.operator, + })); + // Extract label filters from the filters we have already + const labelsMatch = renderLabelsWithoutBrackets(labelFilters); + // Create a matcher using metric names and label filters + return `{${[metricMatch, ...labelsMatch].join(',')}}`; +}; diff --git a/packages/grafana-prometheus/src/language_provider.test.ts b/packages/grafana-prometheus/src/language_provider.test.ts index b8f230a33ab..ec5cba785e5 100644 --- a/packages/grafana-prometheus/src/language_provider.test.ts +++ b/packages/grafana-prometheus/src/language_provider.test.ts @@ -938,5 +938,16 @@ describe('PrometheusLanguageProvider with feature toggle', () => { const result = populateMatchParamsFromQueries(queries); expect(result).toBe('__name__!=""'); }); + + it('should extract the correct matcher for queries with `... or vector(0)`', () => { + const queries: PromQuery[] = [ + { + refId: 'A', + expr: `sum(increase(go_cpu_classes_idle_cpu_seconds_total[$__rate_interval])) or vector(0)`, + }, + ]; + const result = populateMatchParamsFromQueries(queries); + expect(result).toBe('__name__=~"go_cpu_classes_idle_cpu_seconds_total"'); + }); }); }); diff --git a/packages/grafana-prometheus/src/language_provider.ts b/packages/grafana-prometheus/src/language_provider.ts index 2e96e4fd65f..cfedcc9bdd0 100644 --- a/packages/grafana-prometheus/src/language_provider.ts +++ b/packages/grafana-prometheus/src/language_provider.ts @@ -810,7 +810,9 @@ export const populateMatchParamsFromQueries = (queries?: PromQuery[]): string => } if (visualQuery.query.binaryQueries) { visualQuery.query.binaryQueries.forEach((bq) => { - params.push(bq.query.metric); + if (bq.query.metric !== '') { + params.push(bq.query.metric); + } }); } return params;