mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
Prometheus Datasource: Improve Prom query variable editor (#58292)
* add prom query var editor with tests and migrations * fix migration, now query not expr * fix label_values migration * remove comments * fix label_values() variables order * update UI and use more clear language * fix tests * use null coalescing operators * allow users to query label values with label and metric if they have not set there flavor and version * use enums instead of numbers for readability * fix label&metrics switch * update type in qv editor * reuse datasource function to get all label names, getLabelNames(), prev named getTagKeys() * use getLabelNames in the query var editor * make label_values() variables label and metric more readable in the migration * fix tooltip for label_values to remove API reference * clean up tooltips and allow newlines in query_result function * change function wording and exprType to query type/qryType for readability * update prometheus query variable docs * Update public/app/plugins/datasource/prometheus/components/VariableQueryEditor.tsx Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com> --------- Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com>
This commit is contained in:
parent
1f984409a2
commit
eedcd7d5b1
@ -6261,9 +6261,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"public/app/plugins/datasource/prometheus/query_hints.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
|
@ -23,17 +23,17 @@ For an introduction to templating and template variables, refer to the [Templati
|
||||
|
||||
## Use query variables
|
||||
|
||||
You can use variables of the type _Query_ to query Prometheus for a list of metrics, labels, or label values.
|
||||
Use variables of the type _Query_ to query Prometheus for a list of metrics, labels, or label values.
|
||||
|
||||
You can use these Prometheus data source functions in the **Query** input field:
|
||||
Select a Prometheus data source query type and enter the required inputs:
|
||||
|
||||
| Name | Description | Used API endpoints |
|
||||
| ----------------------------- | ----------------------------------------------------------------------- | --------------------------------- |
|
||||
| `label_names()` | Returns a list of label names. | /api/v1/labels |
|
||||
| `label_values(label)` | Returns a list of label values for the `label` in every metric. | /api/v1/label/`label`/values |
|
||||
| `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. | /api/v1/series |
|
||||
| `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
|
||||
| `query_result(query)` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
|
||||
| Query Type | Input(\* required) | Description | Used API endpoints |
|
||||
| -------------- | ------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
||||
| `Label names` | none | Returns a list of all label names. | /api/v1/labels |
|
||||
| `Label values` | `label`\*, `metric` | Returns a list of label values for the `label` in all metrics or the optional metric. | /api/v1/label/`label`/values or /api/v1/series |
|
||||
| `Metrics` | `metric` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
|
||||
| `Query result` | `query` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
|
||||
| `Series query` | `metric`, `label` or both | Returns a list of time series associated with the entered data. | /api/v1/series |
|
||||
|
||||
For details on _metric names_, _label names_, and _label values_, refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels).
|
||||
|
||||
|
@ -0,0 +1,142 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
||||
import { PromVariableQueryEditor, Props } from './VariableQueryEditor';
|
||||
|
||||
const refId = 'PrometheusVariableQueryEditor-VariableQuery';
|
||||
|
||||
describe('PromVariableQueryEditor', () => {
|
||||
let props: Props;
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
datasource: {
|
||||
hasLabelsMatchAPISupport: () => 1,
|
||||
languageProvider: {
|
||||
start: () => Promise.resolve([]),
|
||||
syntax: () => {},
|
||||
getLabelKeys: () => [],
|
||||
metrics: [],
|
||||
},
|
||||
getInitHints: () => [],
|
||||
} as unknown as PrometheusDatasource,
|
||||
query: {
|
||||
refId: 'test',
|
||||
query: 'label_names()',
|
||||
},
|
||||
onRunQuery: () => {},
|
||||
onChange: () => {},
|
||||
history: [],
|
||||
};
|
||||
});
|
||||
|
||||
test('Displays a group of function options', async () => {
|
||||
render(<PromVariableQueryEditor {...props} />);
|
||||
|
||||
const select = screen.getByLabelText('Query type').parentElement!;
|
||||
await userEvent.click(select);
|
||||
|
||||
await waitFor(() => expect(screen.getAllByText('Label names')).toHaveLength(2));
|
||||
await waitFor(() => expect(screen.getByText('Label values')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByText('Metrics')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByText('Query result')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByText('Series query')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
test('Calls onChange for label_names() query', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
props.query = {
|
||||
refId: 'test',
|
||||
query: '',
|
||||
};
|
||||
|
||||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Label names');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith({
|
||||
query: 'label_names()',
|
||||
refId,
|
||||
});
|
||||
});
|
||||
|
||||
test('Does not call onChange for other queries', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Metrics');
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Query result');
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Series query');
|
||||
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Calls onChange for metrics() with argument onBlur', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
props.query = {
|
||||
refId: 'test',
|
||||
query: 'metrics(a)',
|
||||
};
|
||||
|
||||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
const labelSelect = screen.getByLabelText('Metric selector');
|
||||
await userEvent.click(labelSelect);
|
||||
const functionSelect = screen.getByLabelText('Query type').parentElement!;
|
||||
await userEvent.click(functionSelect);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith({
|
||||
query: 'metrics(a)',
|
||||
refId,
|
||||
});
|
||||
});
|
||||
|
||||
test('Calls onChange for query_result() with argument onBlur', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
props.query = {
|
||||
refId: 'test',
|
||||
query: 'query_result(a)',
|
||||
};
|
||||
|
||||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
const labelSelect = screen.getByLabelText('Prometheus Query');
|
||||
await userEvent.click(labelSelect);
|
||||
const functionSelect = screen.getByLabelText('Query type').parentElement!;
|
||||
await userEvent.click(functionSelect);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith({
|
||||
query: 'query_result(a)',
|
||||
refId,
|
||||
});
|
||||
});
|
||||
|
||||
test('Calls onChange for Match[] series with argument onBlur', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
props.query = {
|
||||
refId: 'test',
|
||||
query: '{a: "example"}',
|
||||
};
|
||||
|
||||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
const labelSelect = screen.getByLabelText('Series Query');
|
||||
await userEvent.click(labelSelect);
|
||||
const functionSelect = screen.getByLabelText('Query type').parentElement!;
|
||||
await userEvent.click(functionSelect);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith({
|
||||
query: '{a: "example"}',
|
||||
refId,
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,257 @@
|
||||
import React, { FC, FormEvent, useEffect, useState } from 'react';
|
||||
|
||||
import { QueryEditorProps, SelectableValue } from '@grafana/data';
|
||||
import { InlineField, InlineFieldRow, Input, Select, TextArea } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import {
|
||||
migrateVariableEditorBackToVariableSupport,
|
||||
migrateVariableQueryToEditor,
|
||||
} from '../migrations/variableMigration';
|
||||
import { PromOptions, PromQuery, PromVariableQuery, PromVariableQueryType as QueryType } from '../types';
|
||||
|
||||
export const variableOptions = [
|
||||
{ label: 'Label names', value: QueryType.LabelNames },
|
||||
{ label: 'Label values', value: QueryType.LabelValues },
|
||||
{ label: 'Metrics', value: QueryType.MetricNames },
|
||||
{ label: 'Query result', value: QueryType.VarQueryResult },
|
||||
{ label: 'Series query', value: QueryType.SeriesQuery },
|
||||
];
|
||||
|
||||
export type Props = QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions, PromVariableQuery>;
|
||||
|
||||
const refId = 'PrometheusVariableQueryEditor-VariableQuery';
|
||||
|
||||
export const PromVariableQueryEditor: FC<Props> = ({ onChange, query, datasource }) => {
|
||||
// to select the query type, i.e. label_names, label_values, etc.
|
||||
const [qryType, setQryType] = useState<number | undefined>(undefined);
|
||||
|
||||
// list of variables for each function
|
||||
const [label, setLabel] = useState('');
|
||||
// metric is used for both label_values() and metric()
|
||||
// label_values() metric requires a whole/complete metric
|
||||
// metric() is expected to be a part of a metric string
|
||||
const [metric, setMetric] = useState('');
|
||||
// varQuery is a whole query, can include math/rates/etc
|
||||
const [varQuery, setVarQuery] = useState('');
|
||||
// seriesQuery is only a whole
|
||||
const [seriesQuery, setSeriesQuery] = useState('');
|
||||
|
||||
// list of label names for label_values(), /api/v1/labels, contains the same results as label_names() function
|
||||
const [labelOptions, setLabelOptions] = useState<Array<SelectableValue<string>>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
// Changing from standard to custom variable editor changes the string attr from expr to query
|
||||
const variableQuery = query.query ? migrateVariableQueryToEditor(query.query) : query;
|
||||
|
||||
setQryType(variableQuery.qryType);
|
||||
setLabel(variableQuery.label ?? '');
|
||||
setMetric(variableQuery.metric ?? '');
|
||||
setVarQuery(variableQuery.varQuery ?? '');
|
||||
setSeriesQuery(variableQuery.seriesQuery ?? '');
|
||||
|
||||
// set the migrated label in the label options
|
||||
if (variableQuery.label) {
|
||||
setLabelOptions([{ label: variableQuery.label, value: variableQuery.label }]);
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
// set the label names options for the label values var query
|
||||
useEffect(() => {
|
||||
if (qryType !== QueryType.LabelValues) {
|
||||
return;
|
||||
}
|
||||
|
||||
datasource.getLabelNames().then((labelNames: Array<{ text: string }>) => {
|
||||
setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text })));
|
||||
});
|
||||
}, [datasource, qryType]);
|
||||
|
||||
const onChangeWithVariableString = (qryType: QueryType) => {
|
||||
const queryVar = {
|
||||
qryType: qryType,
|
||||
label,
|
||||
metric,
|
||||
varQuery,
|
||||
seriesQuery,
|
||||
refId: 'PrometheusVariableQueryEditor-VariableQuery',
|
||||
};
|
||||
|
||||
const queryString = migrateVariableEditorBackToVariableSupport(queryVar);
|
||||
|
||||
onChange({
|
||||
query: queryString,
|
||||
refId,
|
||||
});
|
||||
};
|
||||
|
||||
const onQueryTypeChange = (newType: SelectableValue<QueryType>) => {
|
||||
setQryType(newType.value);
|
||||
if (newType.value === QueryType.LabelNames) {
|
||||
onChangeWithVariableString(newType.value);
|
||||
}
|
||||
};
|
||||
|
||||
const onLabelChange = (newLabel: SelectableValue<string>) => {
|
||||
setLabel(newLabel.value ?? '');
|
||||
};
|
||||
|
||||
const onMetricChange = (e: FormEvent<HTMLInputElement>) => {
|
||||
setMetric(e.currentTarget.value);
|
||||
};
|
||||
|
||||
const onVarQueryChange = (e: FormEvent<HTMLTextAreaElement>) => {
|
||||
setVarQuery(e.currentTarget.value);
|
||||
};
|
||||
|
||||
const onSeriesQueryChange = (e: FormEvent<HTMLInputElement>) => {
|
||||
setSeriesQuery(e.currentTarget.value);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (qryType === QueryType.LabelNames) {
|
||||
onChangeWithVariableString(qryType);
|
||||
} else if (qryType === QueryType.LabelValues && label) {
|
||||
onChangeWithVariableString(qryType);
|
||||
} else if (qryType === QueryType.MetricNames && metric) {
|
||||
onChangeWithVariableString(qryType);
|
||||
} else if (qryType === QueryType.VarQueryResult && varQuery) {
|
||||
onChangeWithVariableString(qryType);
|
||||
} else if (qryType === QueryType.SeriesQuery && seriesQuery) {
|
||||
onChangeWithVariableString(qryType);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Query Type"
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>The Prometheus data source plugin provides the following query types for template variables.</div>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select query type"
|
||||
aria-label="Query type"
|
||||
onChange={onQueryTypeChange}
|
||||
onBlur={handleBlur}
|
||||
value={qryType}
|
||||
options={variableOptions}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
{qryType === QueryType.LabelValues && (
|
||||
<>
|
||||
<InlineField
|
||||
label="Label"
|
||||
labelWidth={20}
|
||||
required
|
||||
tooltip={
|
||||
<div>
|
||||
Returns a list of label values for the label name in all metrics unless the metric is specified.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
aria-label="label-select"
|
||||
onChange={onLabelChange}
|
||||
onBlur={handleBlur}
|
||||
value={label}
|
||||
options={labelOptions}
|
||||
width={25}
|
||||
allowCustomValue
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField
|
||||
label="Metric"
|
||||
labelWidth={20}
|
||||
tooltip={<div>Optional: returns a list of label values for the label name in the specified metric.</div>}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Metric selector"
|
||||
placeholder="Optional metric selector"
|
||||
value={metric}
|
||||
onChange={onMetricChange}
|
||||
onBlur={handleBlur}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
</>
|
||||
)}
|
||||
{qryType === QueryType.MetricNames && (
|
||||
<>
|
||||
<InlineField
|
||||
label="Metric Regex"
|
||||
labelWidth={20}
|
||||
tooltip={<div>Returns a list of metrics matching the specified metric regex.</div>}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Metric selector"
|
||||
placeholder="Metric Regex"
|
||||
value={metric}
|
||||
onChange={onMetricChange}
|
||||
onBlur={handleBlur}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
</>
|
||||
)}
|
||||
{qryType === QueryType.VarQueryResult && (
|
||||
<>
|
||||
<InlineField
|
||||
label="Query"
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
Returns a list of Prometheus query results for the query. This can include Prometheus functions, i.e.
|
||||
sum(go_goroutines).
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<TextArea
|
||||
type="text"
|
||||
aria-label="Prometheus Query"
|
||||
placeholder="Prometheus Query"
|
||||
value={varQuery}
|
||||
onChange={onVarQueryChange}
|
||||
onBlur={handleBlur}
|
||||
cols={100}
|
||||
/>
|
||||
</InlineField>
|
||||
</>
|
||||
)}
|
||||
{qryType === QueryType.SeriesQuery && (
|
||||
<>
|
||||
<InlineField
|
||||
label="Series Query"
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
Enter enter a metric with labels, only a metric or only labels, i.e.
|
||||
go_goroutines{instance="localhost:9090"}, go_goroutines, or
|
||||
{instance="localhost:9090"}. Returns a list of time series associated with the
|
||||
entered data.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Series Query"
|
||||
placeholder="Series Query"
|
||||
value={seriesQuery}
|
||||
onChange={onSeriesQueryChange}
|
||||
onBlur={handleBlur}
|
||||
width={100}
|
||||
/>
|
||||
</InlineField>
|
||||
</>
|
||||
)}
|
||||
</InlineFieldRow>
|
||||
);
|
||||
};
|
@ -153,7 +153,7 @@ describe('PrometheusDatasource', () => {
|
||||
).rejects.toMatchObject({
|
||||
message: expect.stringMatching('Browser access'),
|
||||
});
|
||||
await expect(directDs.getTagKeys()).rejects.toMatchObject({
|
||||
await expect(directDs.getLabelNames()).rejects.toMatchObject({
|
||||
message: expect.stringMatching('Browser access'),
|
||||
});
|
||||
await expect(directDs.getTagValues()).rejects.toMatchObject({
|
||||
|
@ -286,7 +286,7 @@ export class PrometheusDatasource
|
||||
hideFromInspector: true,
|
||||
...options,
|
||||
})
|
||||
); // toPromise until we change getTagValues, getTagKeys to Observable
|
||||
); // toPromise until we change getTagValues, getLabelNames to Observable
|
||||
}
|
||||
|
||||
interpolateQueryExpr(value: string | string[] = [], variable: any) {
|
||||
@ -908,7 +908,10 @@ export class PrometheusDatasource
|
||||
);
|
||||
}
|
||||
|
||||
async getTagKeys(options?: any) {
|
||||
// this is used to get label keys, a.k.a label names
|
||||
// it is used in metric_find_query.ts
|
||||
// and in Tempo here grafana/public/app/plugins/datasource/tempo/QueryEditor/ServiceGraphSection.tsx
|
||||
async getLabelNames(options?: any) {
|
||||
if (options?.series) {
|
||||
// Get tags for the provided series only
|
||||
const seriesLabels: Array<Record<string, string[]>> = await Promise.all(
|
||||
|
@ -24,7 +24,7 @@ export default class PrometheusMetricFindQuery {
|
||||
const queryResultRegex = /^query_result\((.+)\)\s*$/;
|
||||
const labelNamesQuery = this.query.match(labelNamesRegex);
|
||||
if (labelNamesQuery) {
|
||||
return this.labelNamesQuery();
|
||||
return this.datasource.getLabelNames();
|
||||
}
|
||||
|
||||
const labelValuesQuery = this.query.match(labelValuesRegex);
|
||||
@ -47,24 +47,12 @@ export default class PrometheusMetricFindQuery {
|
||||
}
|
||||
|
||||
// if query contains full metric name, return metric name and label list
|
||||
const expressions = ['label_values()', 'metrics()', 'query_result()'];
|
||||
if (!expressions.includes(this.query)) {
|
||||
return this.metricNameAndLabelsQuery(this.query);
|
||||
}
|
||||
|
||||
labelNamesQuery() {
|
||||
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
||||
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
||||
const params = {
|
||||
start: start.toString(),
|
||||
end: end.toString(),
|
||||
};
|
||||
|
||||
const url = `/api/v1/labels`;
|
||||
|
||||
return this.datasource.metadataRequest(url, params).then((result: any) => {
|
||||
return _map(result.data.data, (value) => {
|
||||
return { text: value };
|
||||
});
|
||||
});
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
labelValuesQuery(label: string, metric?: string) {
|
||||
|
@ -0,0 +1,104 @@
|
||||
import { PromVariableQuery, PromVariableQueryType as QueryType } from '../types';
|
||||
|
||||
const labelNamesRegex = /^label_names\(\)\s*$/;
|
||||
const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
|
||||
const metricNamesRegex = /^metrics\((.+)\)\s*$/;
|
||||
const queryResultRegex = /^query_result\((.+)\)\s*$/;
|
||||
|
||||
export function migrateVariableQueryToEditor(rawQuery: string | PromVariableQuery): PromVariableQuery {
|
||||
// If not string, we assume PromVariableQuery
|
||||
if (typeof rawQuery !== 'string') {
|
||||
return rawQuery;
|
||||
}
|
||||
|
||||
const queryBase = {
|
||||
refId: 'PrometheusDatasource-VariableQuery',
|
||||
qryType: QueryType.LabelNames,
|
||||
};
|
||||
|
||||
const labelNames = rawQuery.match(labelNamesRegex);
|
||||
if (labelNames) {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.LabelNames,
|
||||
};
|
||||
}
|
||||
|
||||
const labelValues = rawQuery.match(labelValuesRegex);
|
||||
|
||||
if (labelValues) {
|
||||
const label = labelValues[2];
|
||||
const metric = labelValues[1];
|
||||
if (metric) {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.LabelValues,
|
||||
label,
|
||||
metric,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.LabelValues,
|
||||
label,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const metricNames = rawQuery.match(metricNamesRegex);
|
||||
if (metricNames) {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.MetricNames,
|
||||
metric: metricNames[1],
|
||||
};
|
||||
}
|
||||
|
||||
const queryResult = rawQuery.match(queryResultRegex);
|
||||
if (queryResult) {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.VarQueryResult,
|
||||
varQuery: queryResult[1],
|
||||
};
|
||||
}
|
||||
|
||||
// seriesQuery does not have a function and no regex above
|
||||
if (!labelNames && !labelValues && !metricNames && !queryResult) {
|
||||
return {
|
||||
...queryBase,
|
||||
qryType: QueryType.SeriesQuery,
|
||||
seriesQuery: rawQuery,
|
||||
};
|
||||
}
|
||||
|
||||
return queryBase;
|
||||
}
|
||||
|
||||
// migrate it back to a string with the correct varialbes in place
|
||||
export function migrateVariableEditorBackToVariableSupport(QueryVariable: PromVariableQuery): string {
|
||||
switch (QueryVariable.qryType) {
|
||||
case QueryType.LabelNames:
|
||||
return 'label_names()';
|
||||
case QueryType.LabelValues:
|
||||
if (QueryVariable.metric) {
|
||||
return `label_values(${QueryVariable.metric},${QueryVariable.label})`;
|
||||
} else {
|
||||
return `label_values(${QueryVariable.label})`;
|
||||
}
|
||||
case QueryType.MetricNames:
|
||||
return `metrics(${QueryVariable.metric})`;
|
||||
case QueryType.VarQueryResult:
|
||||
const varQuery = removeLineBreaks(QueryVariable.varQuery);
|
||||
return `query_result(${varQuery})`;
|
||||
case QueryType.SeriesQuery:
|
||||
return '' + QueryVariable.seriesQuery;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// allow line breaks in query result textarea
|
||||
function removeLineBreaks(input?: string) {
|
||||
return input ? input.replace(/[\r\n]+/gm, '') : '';
|
||||
}
|
@ -22,6 +22,7 @@ export interface PromQuery extends DataQuery {
|
||||
showingTable?: boolean;
|
||||
/** Code, Builder or Explain */
|
||||
editorMode?: QueryEditorMode;
|
||||
query?: string;
|
||||
}
|
||||
|
||||
export interface PromOptions extends DataSourceJsonData {
|
||||
@ -165,3 +166,21 @@ export enum LegendFormatMode {
|
||||
Verbose = '__verbose',
|
||||
Custom = '__custom',
|
||||
}
|
||||
|
||||
export enum PromVariableQueryType {
|
||||
LabelNames,
|
||||
LabelValues,
|
||||
MetricNames,
|
||||
VarQueryResult,
|
||||
SeriesQuery,
|
||||
}
|
||||
|
||||
export interface PromVariableQuery extends DataQuery {
|
||||
query?: string;
|
||||
expr?: string;
|
||||
qryType?: PromVariableQueryType;
|
||||
label?: string;
|
||||
metric?: string;
|
||||
varQuery?: string;
|
||||
seriesQuery?: string;
|
||||
}
|
||||
|
@ -1,22 +1,17 @@
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
DataQueryRequest,
|
||||
DataQueryResponse,
|
||||
rangeUtil,
|
||||
StandardVariableQuery,
|
||||
StandardVariableSupport,
|
||||
} from '@grafana/data';
|
||||
import { CustomVariableSupport, DataQueryRequest, DataQueryResponse, rangeUtil } from '@grafana/data';
|
||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { getTimeSrv, TimeSrv } from '../../../features/dashboard/services/TimeSrv';
|
||||
|
||||
import { PromVariableQueryEditor } from './components/VariableQueryEditor';
|
||||
import { PrometheusDatasource } from './datasource';
|
||||
import PrometheusMetricFindQuery from './metric_find_query';
|
||||
import { PromQuery } from './types';
|
||||
|
||||
export class PrometheusVariableSupport extends StandardVariableSupport<PrometheusDatasource> {
|
||||
export class PrometheusVariableSupport extends CustomVariableSupport<PrometheusDatasource> {
|
||||
constructor(
|
||||
private readonly datasource: PrometheusDatasource,
|
||||
private readonly templateSrv: TemplateSrv = getTemplateSrv(),
|
||||
@ -26,8 +21,10 @@ export class PrometheusVariableSupport extends StandardVariableSupport<Prometheu
|
||||
this.query = this.query.bind(this);
|
||||
}
|
||||
|
||||
editor = PromVariableQueryEditor;
|
||||
|
||||
query(request: DataQueryRequest<PromQuery>): Observable<DataQueryResponse> {
|
||||
const query = request.targets[0].expr;
|
||||
const query = request.targets[0].query;
|
||||
if (!query) {
|
||||
return of({ data: [] });
|
||||
}
|
||||
@ -48,11 +45,4 @@ export class PrometheusVariableSupport extends StandardVariableSupport<Prometheu
|
||||
|
||||
return metricFindStream.pipe(map((results) => ({ data: results })));
|
||||
}
|
||||
|
||||
toDataQuery(query: StandardVariableQuery): PromQuery {
|
||||
return {
|
||||
refId: 'PrometheusDatasource-VariableQuery',
|
||||
expr: query.query,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export function ServiceGraphSection({
|
||||
const [hasKeys, setHasKeys] = useState<boolean | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
async function fn(ds: PrometheusDatasource) {
|
||||
const keys = await ds.getTagKeys({
|
||||
const keys = await ds.getLabelNames({
|
||||
series: [
|
||||
'traces_service_graph_request_server_seconds_sum',
|
||||
'traces_service_graph_request_total',
|
||||
|
Loading…
Reference in New Issue
Block a user