diff --git a/public/app/plugins/datasource/prometheus/language_provider.ts b/public/app/plugins/datasource/prometheus/language_provider.ts index 71ff5bf5f72..9acc7a33f21 100644 --- a/public/app/plugins/datasource/prometheus/language_provider.ts +++ b/public/app/plugins/datasource/prometheus/language_provider.ts @@ -9,12 +9,11 @@ import { TypeaheadOutput, } from 'app/types/explore'; -import { parseSelector, processLabels } from './language_utils'; +import { parseSelector, processLabels, processHistogramLabels } from './language_utils'; import PromqlSyntax, { FUNCTIONS, RATE_RANGES } from './promql'; const DEFAULT_KEYS = ['job', 'instance']; const EMPTY_SELECTOR = '{}'; -const HISTOGRAM_SELECTOR = '{le!=""}'; // Returns all timeseries for histograms const HISTORY_ITEM_COUNT = 5; const HISTORY_COUNT_CUTOFF = 1000 * 60 * 60 * 24; // 24h @@ -66,8 +65,17 @@ export default class PromQlLanguageProvider extends LanguageProvider { return PromqlSyntax; } - request = (url: string) => { - return this.datasource.metadataRequest(url); + request = async (url: string) => { + try { + const res = await this.datasource.metadataRequest(url); + const body = await (res.data || res.json()); + + return body.data; + } catch (error) { + console.error(error); + } + + return []; }; start = () => { @@ -78,7 +86,22 @@ export default class PromQlLanguageProvider extends LanguageProvider { }; fetchMetrics = async () => { - return this.fetchMetricNames().then(() => [this.fetchHistogramMetrics()]); + this.metrics = await this.fetchMetricNames(); + this.processHistogramMetrics(this.metrics); + + return Promise.resolve([]); + }; + + fetchMetricNames = async (): Promise => { + return this.request('/api/v1/label/__name__/values'); + }; + + processHistogramMetrics = (data: string[]) => { + const { values } = processHistogramLabels(data); + + if (values && values['__name__']) { + this.histogramMetrics = values['__name__'].slice().sort(); + } }; // Keep this DOM-free for testing @@ -293,60 +316,28 @@ export default class PromQlLanguageProvider extends LanguageProvider { return { context, refresher, suggestions }; } - async fetchMetricNames() { - const url = '/api/v1/label/__name__/values'; + fetchLabelValues = async (key: string) => { try { - const res = await this.request(url); - const body = await (res.data || res.json()); - this.metrics = body.data; - } catch (error) { - console.error(error); - } - } - - async fetchHistogramMetrics() { - await this.fetchSeriesLabels(HISTOGRAM_SELECTOR, true); - const histogramSeries = this.labelValues[HISTOGRAM_SELECTOR]; - if (histogramSeries && histogramSeries['__name__']) { - this.histogramMetrics = histogramSeries['__name__'].slice().sort(); - } - } - - async fetchLabelValues(key: string) { - const url = `/api/v1/label/${key}/values`; - try { - const res = await this.request(url); - const body = await (res.data || res.json()); - const exisingValues = this.labelValues[EMPTY_SELECTOR]; + const data = await this.request(`/api/v1/label/${key}/values`); + const existingValues = this.labelValues[EMPTY_SELECTOR]; const values = { - ...exisingValues, - [key]: body.data, - }; - this.labelValues = { - ...this.labelValues, - [EMPTY_SELECTOR]: values, + ...existingValues, + [key]: data, }; + this.labelValues[EMPTY_SELECTOR] = values; } catch (e) { console.error(e); } - } + }; - async fetchSeriesLabels(name: string, withName?: boolean) { - const url = `/api/v1/series?match[]=${name}`; + fetchSeriesLabels = async (name: string, withName?: boolean) => { try { - const res = await this.request(url); - const body = await (res.data || res.json()); - const { keys, values } = processLabels(body.data, withName); - this.labelKeys = { - ...this.labelKeys, - [name]: keys, - }; - this.labelValues = { - ...this.labelValues, - [name]: values, - }; + const data = await this.request(`/api/v1/series?match[]=${name}`); + const { keys, values } = processLabels(data, withName); + this.labelKeys[name] = keys; + this.labelValues[name] = values; } catch (e) { console.error(e); } - } + }; } diff --git a/public/app/plugins/datasource/prometheus/language_utils.ts b/public/app/plugins/datasource/prometheus/language_utils.ts index 6332da26149..f10d9eed422 100644 --- a/public/app/plugins/datasource/prometheus/language_utils.ts +++ b/public/app/plugins/datasource/prometheus/language_utils.ts @@ -1,8 +1,24 @@ export const RATE_RANGES = ['1m', '5m', '10m', '30m', '1h']; -export function processLabels(labels, withName = false) { - const values = {}; - labels.forEach(l => { +export const processHistogramLabels = (labels: string[]) => { + const result = []; + const regexp = new RegExp('_bucket($|:)'); + for (let index = 0; index < labels.length; index++) { + const label = labels[index]; + const isHistogramValue = regexp.test(label); + if (isHistogramValue) { + if (result.indexOf(label) === -1) { + result.push(label); + } + } + } + + return { values: { __name__: result } }; +}; + +export function processLabels(labels: any, withName = false) { + const values: { [key: string]: string[] } = {}; + labels.forEach((l: any) => { const { __name__, ...rest } = l; if (withName) { values['__name__'] = values['__name__'] || []; @@ -62,7 +78,7 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any // Extract clean labels to form clean selector, incomplete labels are dropped const selector = query.slice(prefixOpen, suffixClose); - const labels = {}; + const labels: { [key: string]: { value: string; operator: string } } = {}; selector.replace(labelRegexp, (_, key, operator, value) => { labels[key] = { value, operator }; return '';