mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Add off switch for metric/label name lookup (#24034)
* Prometheus: Add off switch for metric/label name lookup This will help users with amounts of metric name data that is too much for a browser to handle. Autocomplete will be disabled and metrics chooser hidden, since obviously both rely on this data. Fixes #22702 * Use onUpdateDatasourceJsonDataOptionChecked ... from `@grafana/data`. Refactor naming to faciliate its use and stick with prop names as passed down from `ConfigEditor`. PLUS: - Rephrase switch label, add a tooltip and reduce the size of the to what "Custom query parameters" originally was. - Change `languageProvider` type in `PromQueryField`. * Put language provider back in Functions and history still work, even when metrics lookup gets disabled. Also: Rewording of setting. * Display a message when lookup got disabled manually * Call property for setting disableMetricsLookup * Show disabled metrics chooser instead of warning
This commit is contained in:
parent
42ba13b346
commit
de0e1b2c58
@ -4,6 +4,9 @@ import RCCascader from 'rc-cascader';
|
||||
import React from 'react';
|
||||
import PromQlLanguageProvider, { DEFAULT_LOOKUP_METRICS_THRESHOLD } from '../language_provider';
|
||||
import PromQueryField, { groupMetricsByPrefix, RECORDING_RULES_GROUP } from './PromQueryField';
|
||||
import { ButtonCascader } from '@grafana/ui';
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { PromOptions } from '../types';
|
||||
|
||||
describe('PromQueryField', () => {
|
||||
beforeAll(() => {
|
||||
@ -11,6 +14,47 @@ describe('PromQueryField', () => {
|
||||
window.getSelection = () => {};
|
||||
});
|
||||
|
||||
it('renders metrics chooser regularly if lookups are not disabled in the datasource settings', () => {
|
||||
const datasource = ({
|
||||
languageProvider: {
|
||||
start: () => Promise.resolve([]),
|
||||
},
|
||||
} as unknown) as DataSourceInstanceSettings<PromOptions>;
|
||||
|
||||
const queryField = mount(
|
||||
<PromQueryField
|
||||
// @ts-ignore
|
||||
datasource={datasource}
|
||||
query={{ expr: '', refId: '' }}
|
||||
onRunQuery={() => {}}
|
||||
onChange={() => {}}
|
||||
history={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(queryField.find(ButtonCascader).length).toBe(1);
|
||||
});
|
||||
|
||||
it('renders a disabled metrics chooser if lookups are disabled in datasource settings', () => {
|
||||
const queryField = mount(
|
||||
<PromQueryField
|
||||
// @ts-ignore
|
||||
datasource={{ lookupsDisabled: true }}
|
||||
query={{ expr: '', refId: '' }}
|
||||
onRunQuery={() => {}}
|
||||
onChange={() => {}}
|
||||
history={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
queryField
|
||||
.find(ButtonCascader)
|
||||
.find('button')
|
||||
.props().disabled
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('refreshes metrics when the data source changes', async () => {
|
||||
const metrics = ['foo', 'bar'];
|
||||
const languageProvider = ({
|
||||
|
@ -25,13 +25,19 @@ const HISTOGRAM_GROUP = '__histograms__';
|
||||
const PRISM_SYNTAX = 'promql';
|
||||
export const RECORDING_RULES_GROUP = '__recording_rules__';
|
||||
|
||||
function getChooserText(hasSyntax: boolean, metrics: string[]) {
|
||||
function getChooserText(metricsLookupDisabled: boolean, hasSyntax: boolean, metrics: string[]) {
|
||||
if (metricsLookupDisabled) {
|
||||
return '(Disabled)';
|
||||
}
|
||||
|
||||
if (!hasSyntax) {
|
||||
return 'Loading metrics...';
|
||||
}
|
||||
|
||||
if (metrics && metrics.length === 0) {
|
||||
return '(No metrics found)';
|
||||
}
|
||||
|
||||
return 'Metrics';
|
||||
}
|
||||
|
||||
@ -250,12 +256,10 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
|
||||
onUpdateLanguage = () => {
|
||||
const {
|
||||
histogramMetrics,
|
||||
metrics,
|
||||
metricsMetadata,
|
||||
lookupsDisabled,
|
||||
lookupMetricsThreshold,
|
||||
} = this.props.datasource.languageProvider;
|
||||
datasource,
|
||||
datasource: { languageProvider },
|
||||
} = this.props;
|
||||
const { histogramMetrics, metrics, metricsMetadata, lookupMetricsThreshold } = languageProvider;
|
||||
|
||||
if (!metrics) {
|
||||
return;
|
||||
@ -274,7 +278,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
|
||||
// Hint for big disabled lookups
|
||||
let hint: QueryHint;
|
||||
if (lookupsDisabled) {
|
||||
if (!datasource.lookupsDisabled && languageProvider.lookupsDisabled) {
|
||||
hint = {
|
||||
label: `Dynamic label lookup is disabled for datasources with more than ${lookupMetricsThreshold} metrics.`,
|
||||
type: 'INFO',
|
||||
@ -308,13 +312,14 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
|
||||
render() {
|
||||
const {
|
||||
datasource,
|
||||
datasource: { languageProvider },
|
||||
query,
|
||||
ExtraFieldElement,
|
||||
} = this.props;
|
||||
const { metricsOptions, syntaxLoaded, hint } = this.state;
|
||||
const cleanText = languageProvider ? languageProvider.cleanText : undefined;
|
||||
const chooserText = getChooserText(syntaxLoaded, metricsOptions);
|
||||
const chooserText = getChooserText(datasource.lookupsDisabled, syntaxLoaded, metricsOptions);
|
||||
const buttonDisabled = !(syntaxLoaded && metricsOptions && metricsOptions.length > 0);
|
||||
|
||||
return (
|
||||
|
@ -16,7 +16,7 @@ export const ConfigEditor = (props: Props) => {
|
||||
onChange={onOptionsChange}
|
||||
/>
|
||||
|
||||
<PromSettings value={options} onChange={onOptionsChange} />
|
||||
<PromSettings options={options} onOptionsChange={onOptionsChange} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,11 @@
|
||||
import React, { SyntheticEvent } from 'react';
|
||||
import { EventsWithValidation, InlineFormLabel, regexValidation, LegacyForms } from '@grafana/ui';
|
||||
const { Select, Input, FormField } = LegacyForms;
|
||||
import { DataSourceSettings, SelectableValue } from '@grafana/data';
|
||||
const { Select, Input, FormField, Switch } = LegacyForms;
|
||||
import {
|
||||
SelectableValue,
|
||||
onUpdateDatasourceJsonDataOptionChecked,
|
||||
DataSourcePluginOptionsEditorProps,
|
||||
} from '@grafana/data';
|
||||
import { PromOptions } from '../types';
|
||||
|
||||
const httpOptions = [
|
||||
@ -9,13 +13,10 @@ const httpOptions = [
|
||||
{ value: 'POST', label: 'POST' },
|
||||
];
|
||||
|
||||
type Props = {
|
||||
value: DataSourceSettings<PromOptions>;
|
||||
onChange: (value: DataSourceSettings<PromOptions>) => void;
|
||||
};
|
||||
type Props = Pick<DataSourcePluginOptionsEditorProps<PromOptions>, 'options' | 'onOptionsChange'>;
|
||||
|
||||
export const PromSettings = (props: Props) => {
|
||||
const { value, onChange } = props;
|
||||
const { options, onOptionsChange } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -28,10 +29,10 @@ export const PromSettings = (props: Props) => {
|
||||
inputEl={
|
||||
<Input
|
||||
className="width-6"
|
||||
value={value.jsonData.timeInterval}
|
||||
value={options.jsonData.timeInterval}
|
||||
spellCheck={false}
|
||||
placeholder="15s"
|
||||
onChange={onChangeHandler('timeInterval', value, onChange)}
|
||||
onChange={onChangeHandler('timeInterval', options, onOptionsChange)}
|
||||
validationEvents={promSettingsValidationEvents}
|
||||
/>
|
||||
}
|
||||
@ -47,8 +48,8 @@ export const PromSettings = (props: Props) => {
|
||||
inputEl={
|
||||
<Input
|
||||
className="width-6"
|
||||
value={value.jsonData.queryTimeout}
|
||||
onChange={onChangeHandler('queryTimeout', value, onChange)}
|
||||
value={options.jsonData.queryTimeout}
|
||||
onChange={onChangeHandler('queryTimeout', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="60s"
|
||||
validationEvents={promSettingsValidationEvents}
|
||||
@ -67,14 +68,23 @@ export const PromSettings = (props: Props) => {
|
||||
</InlineFormLabel>
|
||||
<Select
|
||||
options={httpOptions}
|
||||
value={httpOptions.find(o => o.value === value.jsonData.httpMethod)}
|
||||
onChange={onChangeHandler('httpMethod', value, onChange)}
|
||||
value={httpOptions.find(o => o.value === options.jsonData.httpMethod)}
|
||||
onChange={onChangeHandler('httpMethod', options, onOptionsChange)}
|
||||
width={7}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="page-heading">Misc</h3>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form">
|
||||
<Switch
|
||||
checked={options.jsonData.disableMetricsLookup}
|
||||
label="Disable metrics lookup"
|
||||
labelClass="width-14"
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableMetricsLookup')}
|
||||
tooltip="Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances."
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<FormField
|
||||
@ -84,8 +94,8 @@ export const PromSettings = (props: Props) => {
|
||||
inputEl={
|
||||
<Input
|
||||
className="width-25"
|
||||
value={value.jsonData.customQueryParameters}
|
||||
onChange={onChangeHandler('customQueryParameters', value, onChange)}
|
||||
value={options.jsonData.customQueryParameters}
|
||||
onChange={onChangeHandler('customQueryParameters', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="Example: max_source_resolution=5m&timeout=10"
|
||||
/>
|
||||
@ -119,13 +129,15 @@ export const getValueFromEventItem = (eventItem: SyntheticEvent<HTMLInputElement
|
||||
return (eventItem as SelectableValue<string>).value;
|
||||
};
|
||||
|
||||
const onChangeHandler = (key: keyof PromOptions, value: Props['value'], onChange: Props['onChange']) => (
|
||||
eventItem: SyntheticEvent<HTMLInputElement> | SelectableValue<string>
|
||||
) => {
|
||||
onChange({
|
||||
...value,
|
||||
const onChangeHandler = (
|
||||
key: keyof PromOptions,
|
||||
options: Props['options'],
|
||||
onOptionsChange: Props['onOptionsChange']
|
||||
) => (eventItem: SyntheticEvent<HTMLInputElement> | SelectableValue<string>) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
jsonData: {
|
||||
...value.jsonData,
|
||||
...options.jsonData,
|
||||
[key]: getValueFromEventItem(eventItem),
|
||||
},
|
||||
});
|
||||
|
@ -83,6 +83,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
queryTimeout: string;
|
||||
httpMethod: string;
|
||||
languageProvider: PrometheusLanguageProvider;
|
||||
lookupsDisabled: boolean;
|
||||
resultTransformer: ResultTransformer;
|
||||
customQueryParameters: any;
|
||||
|
||||
@ -101,6 +102,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
this.resultTransformer = new ResultTransformer(templateSrv);
|
||||
this.ruleMappings = {};
|
||||
this.languageProvider = new PrometheusLanguageProvider(this);
|
||||
this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup;
|
||||
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
|
||||
}
|
||||
|
||||
|
@ -112,10 +112,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
};
|
||||
|
||||
start = async (): Promise<any[]> => {
|
||||
if (this.datasource.lookupsDisabled) {
|
||||
return [];
|
||||
}
|
||||
|
||||
this.metrics = await this.request('/api/v1/label/__name__/values', []);
|
||||
this.lookupsDisabled = this.metrics.length > this.lookupMetricsThreshold;
|
||||
this.metricsMetadata = await this.request('/api/v1/metadata', {});
|
||||
this.processHistogramMetrics(this.metrics);
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ export interface PromOptions extends DataSourceJsonData {
|
||||
httpMethod: string;
|
||||
directUrl: string;
|
||||
customQueryParameters?: string;
|
||||
disableMetricsLookup?: boolean;
|
||||
}
|
||||
|
||||
export interface PromQueryRequest extends PromQuery {
|
||||
|
Loading…
Reference in New Issue
Block a user