mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Add time range parameters to labels API (#27548)
* Add time range parameters to labels API Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Fix minor issues Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Add range to explore component Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Add range to query component Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Cache metric names for time range Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Update public/app/plugins/datasource/prometheus/components/PromQueryField.tsx Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * Remove unused method Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Only compare the ranges Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com> * Update public/app/plugins/datasource/prometheus/components/PromQueryField.tsx Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import PromExploreQueryEditor from './PromExploreQueryEditor';
|
import PromExploreQueryEditor from './PromExploreQueryEditor';
|
||||||
import { PrometheusDatasource } from '../datasource';
|
import { PrometheusDatasource } from '../datasource';
|
||||||
import { PromQuery } from '../types';
|
import { PromQuery } from '../types';
|
||||||
import { LoadingState, PanelData, toUtc } from '@grafana/data';
|
import { LoadingState, PanelData, toUtc, TimeRange } from '@grafana/data';
|
||||||
|
|
||||||
const setup = (renderMethod: any, propOverrides?: object) => {
|
const setup = (renderMethod: any, propOverrides?: object) => {
|
||||||
const datasourceMock: unknown = {};
|
const datasourceMock: unknown = {};
|
||||||
@@ -12,6 +12,14 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
|||||||
const onRunQuery = jest.fn();
|
const onRunQuery = jest.fn();
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
const query: PromQuery = { expr: '', refId: 'A', interval: '1s' };
|
const query: PromQuery = { expr: '', refId: 'A', interval: '1s' };
|
||||||
|
const range: TimeRange = {
|
||||||
|
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
raw: {
|
||||||
|
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
const data: PanelData = {
|
const data: PanelData = {
|
||||||
state: LoadingState.NotStarted,
|
state: LoadingState.NotStarted,
|
||||||
series: [],
|
series: [],
|
||||||
@@ -50,6 +58,7 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
|||||||
const props: any = {
|
const props: any = {
|
||||||
query,
|
query,
|
||||||
data,
|
data,
|
||||||
|
range,
|
||||||
datasource,
|
datasource,
|
||||||
exploreMode,
|
exploreMode,
|
||||||
history,
|
history,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { PromExploreExtraField } from './PromExploreExtraField';
|
|||||||
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
||||||
|
|
||||||
export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||||
const { query, data, datasource, history, onChange, onRunQuery } = props;
|
const { range, query, data, datasource, history, onChange, onRunQuery } = props;
|
||||||
|
|
||||||
function onChangeQueryStep(value: string) {
|
function onChangeQueryStep(value: string) {
|
||||||
const { query, onChange } = props;
|
const { query, onChange } = props;
|
||||||
@@ -49,6 +49,7 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
|||||||
<PromQueryField
|
<PromQueryField
|
||||||
datasource={datasource}
|
datasource={datasource}
|
||||||
query={query}
|
query={query}
|
||||||
|
range={range}
|
||||||
onRunQuery={onRunQuery}
|
onRunQuery={onRunQuery}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onBlur={() => {}}
|
onBlur={() => {}}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { datasource, query, data } = this.props;
|
const { datasource, query, range, data } = this.props;
|
||||||
const { formatOption, instant, interval, intervalFactorOption, legendFormat } = this.state;
|
const { formatOption, instant, interval, intervalFactorOption, legendFormat } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -104,6 +104,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
|||||||
<PromQueryField
|
<PromQueryField
|
||||||
datasource={datasource}
|
datasource={datasource}
|
||||||
query={query}
|
query={query}
|
||||||
|
range={range}
|
||||||
onRunQuery={this.onRunQuery}
|
onRunQuery={this.onRunQuery}
|
||||||
onChange={this.onFieldChange}
|
onChange={this.onFieldChange}
|
||||||
history={[]}
|
history={[]}
|
||||||
|
|||||||
@@ -17,7 +17,14 @@ import Prism from 'prismjs';
|
|||||||
// dom also includes Element polyfills
|
// dom also includes Element polyfills
|
||||||
import { PromQuery, PromOptions, PromMetricsMetadata } from '../types';
|
import { PromQuery, PromOptions, PromMetricsMetadata } from '../types';
|
||||||
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
|
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
|
||||||
import { ExploreQueryFieldProps, QueryHint, isDataFrame, toLegacyResponseData, HistoryItem } from '@grafana/data';
|
import {
|
||||||
|
ExploreQueryFieldProps,
|
||||||
|
QueryHint,
|
||||||
|
isDataFrame,
|
||||||
|
toLegacyResponseData,
|
||||||
|
HistoryItem,
|
||||||
|
AbsoluteTimeRange,
|
||||||
|
} from '@grafana/data';
|
||||||
import { DOMUtil, SuggestionsState } from '@grafana/ui';
|
import { DOMUtil, SuggestionsState } from '@grafana/ui';
|
||||||
import { PrometheusDatasource } from '../datasource';
|
import { PrometheusDatasource } from '../datasource';
|
||||||
|
|
||||||
@@ -163,9 +170,24 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
datasource: { languageProvider },
|
datasource: { languageProvider },
|
||||||
|
range,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (languageProvider !== prevProps.datasource.languageProvider) {
|
let refreshed = false;
|
||||||
|
if (range && prevProps.range) {
|
||||||
|
const absoluteRange: AbsoluteTimeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
|
||||||
|
const prevAbsoluteRange: AbsoluteTimeRange = {
|
||||||
|
from: prevProps.range.from.valueOf(),
|
||||||
|
to: prevProps.range.to.valueOf(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!_.isEqual(absoluteRange, prevAbsoluteRange)) {
|
||||||
|
this.refreshMetrics();
|
||||||
|
refreshed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!refreshed && languageProvider !== prevProps.datasource.languageProvider) {
|
||||||
this.refreshMetrics();
|
this.refreshMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,5 +57,15 @@ exports[`PromExploreQueryEditor should render component 1`] = `
|
|||||||
"refId": "A",
|
"refId": "A",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
range={
|
||||||
|
Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -203,26 +203,6 @@ describe('PrometheusDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing performSuggestQuery', () => {
|
|
||||||
it('should cache response', async () => {
|
|
||||||
fetchMock.mockImplementation(() =>
|
|
||||||
of({
|
|
||||||
status: 'success',
|
|
||||||
data: { data: ['value1', 'value2', 'value3'] },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let results = await ds.performSuggestQuery('value', true);
|
|
||||||
|
|
||||||
expect(results).toHaveLength(3);
|
|
||||||
|
|
||||||
fetchMock.mockImplementation(jest.fn());
|
|
||||||
results = await ds.performSuggestQuery('value', true);
|
|
||||||
|
|
||||||
expect(results).toHaveLength(3);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When converting prometheus histogram to heatmap format', () => {
|
describe('When converting prometheus histogram to heatmap format', () => {
|
||||||
let query: any;
|
let query: any;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
import LRU from 'lru-cache';
|
||||||
// Services & Utils
|
// Services & Utils
|
||||||
import {
|
import {
|
||||||
AnnotationEvent,
|
AnnotationEvent,
|
||||||
@@ -66,7 +67,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
|||||||
directUrl: string;
|
directUrl: string;
|
||||||
basicAuth: any;
|
basicAuth: any;
|
||||||
withCredentials: any;
|
withCredentials: any;
|
||||||
metricsNameCache: any;
|
metricsNameCache = new LRU<string, string[]>(10);
|
||||||
interval: string;
|
interval: string;
|
||||||
queryTimeout: string;
|
queryTimeout: string;
|
||||||
httpMethod: string;
|
httpMethod: string;
|
||||||
@@ -527,20 +528,6 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
|||||||
return error;
|
return error;
|
||||||
};
|
};
|
||||||
|
|
||||||
async performSuggestQuery(query: string, cache = false) {
|
|
||||||
if (cache && this.metricsNameCache?.expire > Date.now()) {
|
|
||||||
return this.metricsNameCache.data.filter((metricName: any) => metricName.indexOf(query) !== 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response: PromLabelQueryResponse = await this.metadataRequest('/api/v1/label/__name__/values');
|
|
||||||
this.metricsNameCache = {
|
|
||||||
data: response.data.data,
|
|
||||||
expire: Date.now() + 60 * 1000,
|
|
||||||
};
|
|
||||||
|
|
||||||
return response.data.data.filter(metricName => metricName.indexOf(query) !== 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
metricFindQuery(query: string) {
|
metricFindQuery(query: string) {
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
|
|||||||
@@ -114,7 +114,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.metrics = await this.request('/api/v1/label/__name__/values', []);
|
const tRange = this.datasource.getTimeRange();
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: tRange['start'].toString(),
|
||||||
|
end: tRange['end'].toString(),
|
||||||
|
});
|
||||||
|
const url = `/api/v1/label/__name__/values?${params.toString()}`;
|
||||||
|
|
||||||
|
this.metrics = await this.request(url, []);
|
||||||
this.lookupsDisabled = this.metrics.length > this.lookupMetricsThreshold;
|
this.lookupsDisabled = this.metrics.length > this.lookupMetricsThreshold;
|
||||||
this.metricsMetadata = fixSummariesMetadata(await this.request('/api/v1/metadata', {}));
|
this.metricsMetadata = fixSummariesMetadata(await this.request('/api/v1/metadata', {}));
|
||||||
this.processHistogramMetrics(this.metrics);
|
this.processHistogramMetrics(this.metrics);
|
||||||
@@ -404,7 +411,13 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchLabelValues = async (key: string): Promise<Record<string, string[]>> => {
|
fetchLabelValues = async (key: string): Promise<Record<string, string[]>> => {
|
||||||
const data = await this.request(`/api/v1/label/${key}/values`, []);
|
const tRange = this.datasource.getTimeRange();
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: tRange['start'].toString(),
|
||||||
|
end: tRange['end'].toString(),
|
||||||
|
});
|
||||||
|
const url = `/api/v1/label/${key}/values?${params.toString()}`;
|
||||||
|
const data = await this.request(url, []);
|
||||||
return { [key]: data };
|
return { [key]: data };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ describe('PrometheusMetricFindQuery', () => {
|
|||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
expect(fetchMock).toHaveBeenCalledWith({
|
expect(fetchMock).toHaveBeenCalledWith({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'proxied/api/v1/labels',
|
url: `proxied/api/v1/labels?start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
hideFromInspector: true,
|
hideFromInspector: true,
|
||||||
headers: {},
|
headers: {},
|
||||||
});
|
});
|
||||||
@@ -95,7 +95,7 @@ describe('PrometheusMetricFindQuery', () => {
|
|||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
expect(fetchMock).toHaveBeenCalledWith({
|
expect(fetchMock).toHaveBeenCalledWith({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'proxied/api/v1/label/resource/values',
|
url: `proxied/api/v1/label/resource/values?start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
hideFromInspector: true,
|
hideFromInspector: true,
|
||||||
headers: {},
|
headers: {},
|
||||||
});
|
});
|
||||||
@@ -190,7 +190,7 @@ describe('PrometheusMetricFindQuery', () => {
|
|||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
expect(fetchMock).toHaveBeenCalledWith({
|
expect(fetchMock).toHaveBeenCalledWith({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'proxied/api/v1/label/__name__/values',
|
url: `proxied/api/v1/label/__name__/values?start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
hideFromInspector: true,
|
hideFromInspector: true,
|
||||||
headers: {},
|
headers: {},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,7 +48,15 @@ export default class PrometheusMetricFindQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
labelNamesQuery() {
|
labelNamesQuery() {
|
||||||
const url = '/api/v1/labels';
|
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
||||||
|
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: start.toString(),
|
||||||
|
end: end.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `/api/v1/labels?${params.toString()}`;
|
||||||
|
|
||||||
return this.datasource.metadataRequest(url).then((result: any) => {
|
return this.datasource.metadataRequest(url).then((result: any) => {
|
||||||
return _.map(result.data.data, value => {
|
return _.map(result.data.data, value => {
|
||||||
return { text: value };
|
return { text: value };
|
||||||
@@ -57,11 +65,18 @@ export default class PrometheusMetricFindQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
labelValuesQuery(label: string, metric?: string) {
|
labelValuesQuery(label: string, metric?: string) {
|
||||||
|
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
||||||
|
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
||||||
|
|
||||||
let url: string;
|
let url: string;
|
||||||
|
|
||||||
if (!metric) {
|
if (!metric) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: start.toString(),
|
||||||
|
end: end.toString(),
|
||||||
|
});
|
||||||
// return label values globally
|
// return label values globally
|
||||||
url = '/api/v1/label/' + label + '/values';
|
url = `/api/v1/label/${label}/values?${params.toString()}`;
|
||||||
|
|
||||||
return this.datasource.metadataRequest(url).then((result: any) => {
|
return this.datasource.metadataRequest(url).then((result: any) => {
|
||||||
return _.map(result.data.data, value => {
|
return _.map(result.data.data, value => {
|
||||||
@@ -69,8 +84,6 @@ export default class PrometheusMetricFindQuery {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
|
||||||
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
'match[]': metric,
|
'match[]': metric,
|
||||||
start: start.toString(),
|
start: start.toString(),
|
||||||
@@ -96,7 +109,13 @@ export default class PrometheusMetricFindQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metricNameQuery(metricFilterPattern: string) {
|
metricNameQuery(metricFilterPattern: string) {
|
||||||
const url = '/api/v1/label/__name__/values';
|
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
||||||
|
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: start.toString(),
|
||||||
|
end: end.toString(),
|
||||||
|
});
|
||||||
|
const url = `/api/v1/label/__name__/values?${params.toString()}`;
|
||||||
|
|
||||||
return this.datasource.metadataRequest(url).then((result: any) => {
|
return this.datasource.metadataRequest(url).then((result: any) => {
|
||||||
return _.chain(result.data.data)
|
return _.chain(result.data.data)
|
||||||
|
|||||||
Reference in New Issue
Block a user