import { AdHocVariableFilter, RawTimeRange, Scope } from '@grafana/data'; import { getPrometheusTime, PromQueryModeller, utf8Support } from '@grafana/prometheus'; import { config, getBackendSrv } from '@grafana/runtime'; import { limitOtelMatchTerms } from '../otel/util'; import { callSuggestionsApi, SuggestionsResponse } from '../utils'; const LIMIT_REACHED = 'results truncated due to limit'; const queryModeller = new PromQueryModeller(); export async function getMetricNames( dataSourceUid: string, timeRange: RawTimeRange, scopes: Scope[], filters: AdHocVariableFilter[], jobs: string[], instances: string[], limit?: number ): Promise { if (!config.featureToggles.enableScopesInMetricsExplore) { return await getMetricNamesWithoutScopes(dataSourceUid, timeRange, filters, jobs, instances, limit); } return getMetricNamesWithScopes(dataSourceUid, timeRange, scopes, filters, jobs, instances, limit); } export async function getMetricNamesWithoutScopes( dataSourceUid: string, timeRange: RawTimeRange, adhocFilters: AdHocVariableFilter[], jobs: string[], instances: string[], limit?: number ) { const matchTerms = config.featureToggles.prometheusSpecialCharsInLabelValues ? adhocFilters.map((filter) => removeBrackets(queryModeller.renderLabels([{ label: filter.key, op: filter.operator, value: filter.value }])) ) : adhocFilters.map((filter) => `${utf8Support(filter.key)}${filter.operator}"${filter.value}"`); let missingOtelTargets = false; if (jobs.length > 0 && instances.length > 0) { const otelMatches = limitOtelMatchTerms(matchTerms, jobs, instances); missingOtelTargets = otelMatches.missingOtelTargets; matchTerms.push(otelMatches.jobsRegex); matchTerms.push(otelMatches.instancesRegex); } const filters = `{${matchTerms.join(',')}}`; const url = `/api/datasources/uid/${dataSourceUid}/resources/api/v1/label/__name__/values`; const params: Record = { start: getPrometheusTime(timeRange.from, false), end: getPrometheusTime(timeRange.to, true), ...(filters && filters !== '{}' ? { 'match[]': filters } : {}), ...(limit ? { limit } : {}), }; const response = await getBackendSrv().get(url, params, 'metrics-drilldown-names'); if (limit && response.warnings?.includes(LIMIT_REACHED)) { return { ...response, limitReached: true, missingOtelTargets }; } return { ...response, limitReached: false, missingOtelTargets }; } export async function getMetricNamesWithScopes( dataSourceUid: string, timeRange: RawTimeRange, scopes: Scope[], filters: AdHocVariableFilter[], jobs: string[], instances: string[], limit?: number ) { const response = await callSuggestionsApi( dataSourceUid, timeRange, scopes, filters, '__name__', limit, 'metrics-drilldown-names' ); if (jobs.length > 0 && instances.length > 0) { filters.push({ key: 'job', operator: '=~', value: jobs?.join('|') || '', }); filters.push({ key: 'instance', operator: '=~', value: instances?.join('|') || '', }); } return { ...response.data, limitReached: !!limit && !!response.data.warnings?.includes(LIMIT_REACHED), missingOtelTargets: false, }; } function removeBrackets(input: string): string { const match = input.match(/^\{(.*)\}$/); // extract the content inside the brackets return match?.[1] ?? ''; }