Prometheus: Remove the auto range vector option (#45715)

* Prometheus: Remove auto range option

* Prometheus: Remove auto range option

* Overhaul of range vector operations and default param

* Make sure label is string
This commit is contained in:
Torkel Ödegaard 2022-02-25 11:18:08 +01:00 committed by GitHub
parent 9d61dcb02b
commit f7291ce270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 112 deletions

View File

@ -134,7 +134,7 @@ function createRangeOperation(name: string): QueryBuilderOperationDef {
id: name,
name: getPromAndLokiOperationDisplayName(name),
params: [getRangeVectorParamDef()],
defaultParams: ['auto'],
defaultParams: ['$__interval'],
alternativesKey: 'range function',
category: LokiVisualQueryOperationCategory.RangeFunctions,
renderer: operationWithRangeVectorRenderer,
@ -142,7 +142,7 @@ function createRangeOperation(name: string): QueryBuilderOperationDef {
explainHandler: (op, def) => {
let opDocs = FUNCTIONS.find((x) => x.insertText === op.id)?.documentation ?? '';
if (op.params[0] === 'auto' || op.params[0] === '$__interval') {
if (op.params[0] === '$__interval') {
return `${opDocs} \`$__interval\` is variable that will be replaced with a calculated interval based on **Max data points**, **Min interval** and query time range. You find these options you find under **Query options** at the right of the data source select dropdown.`;
} else {
return `${opDocs} The [range vector](https://grafana.com/docs/loki/latest/logql/metric_queries/#range-vector-aggregation) is set to \`${op.params[0]}\`.`;
@ -170,9 +170,9 @@ function createAggregationOperation(name: string): QueryBuilderOperationDef {
function getRangeVectorParamDef(): QueryBuilderOperationParamDef {
return {
name: 'Range vector',
name: 'Range',
type: 'string',
options: ['auto', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'],
options: ['$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'],
};
}
@ -181,12 +181,7 @@ function operationWithRangeVectorRenderer(
def: QueryBuilderOperationDef,
innerExpr: string
) {
let rangeVector = (model.params ?? [])[0] ?? 'auto';
if (rangeVector === 'auto') {
rangeVector = '$__interval';
}
let rangeVector = (model.params ?? [])[0] ?? '$__interval';
return `${def.id}(${innerExpr} [${rangeVector}])`;
}

View File

@ -105,7 +105,7 @@ describe('PromQueryModeller', () => {
modeller.renderQuery({
metric: 'metric',
labels: [{ label: 'pod', op: '=', value: 'A' }],
operations: [{ id: PromOperationId.Rate, params: ['auto'] }],
operations: [{ id: PromOperationId.Rate, params: ['$__rate_interval'] }],
})
).toBe('rate(metric{pod="A"}[$__rate_interval])');
});
@ -115,9 +115,9 @@ describe('PromQueryModeller', () => {
modeller.renderQuery({
metric: 'metric',
labels: [{ label: 'pod', op: '=', value: 'A' }],
operations: [{ id: PromOperationId.Increase, params: ['auto'] }],
operations: [{ id: PromOperationId.Increase, params: ['$__interval'] }],
})
).toBe('increase(metric{pod="A"}[$__rate_interval])');
).toBe('increase(metric{pod="A"}[$__interval])');
});
it('Can render rate with custom range-vector', () => {
@ -283,18 +283,18 @@ describe('PromQueryModeller', () => {
modeller.renderQuery({
metric: 'metric_a',
labels: [],
operations: [{ id: 'holt_winters', params: ['auto', 0.5, 0.5] }],
operations: [{ id: 'holt_winters', params: ['5m', 0.5, 0.5] }],
})
).toBe('holt_winters(metric_a[$__rate_interval], 0.5, 0.5)');
).toBe('holt_winters(metric_a[5m], 0.5, 0.5)');
});
it('Can render functions that require parameters left of a range', () => {
expect(
modeller.renderQuery({
metric: 'metric_a',
labels: [],
operations: [{ id: 'quantile_over_time', params: ['auto', 1] }],
operations: [{ id: 'quantile_over_time', params: ['5m', 1] }],
})
).toBe('quantile_over_time(1, metric_a[$__rate_interval])');
).toBe('quantile_over_time(1, metric_a[5m])');
});
it('Can render the label_join function', () => {
expect(

View File

@ -5,6 +5,7 @@ import {
defaultAddOperationHandler,
functionRendererLeft,
getPromAndLokiOperationDisplayName,
getRangeVectorParamDef,
} from './shared/operationUtils';
import { QueryBuilderOperation, QueryBuilderOperationDef, QueryBuilderOperationParamDef } from './shared/types';
import { PromVisualQueryOperationCategory, PromOperationId } from './types';
@ -25,6 +26,7 @@ export function getAggregationOperations(): QueryBuilderOperationDef[] {
createAggregationOverTime(PromOperationId.CountOverTime),
createAggregationOverTime(PromOperationId.LastOverTime),
createAggregationOverTime(PromOperationId.PresentOverTime),
createAggregationOverTime(PromOperationId.AbsentOverTime),
createAggregationOverTime(PromOperationId.StddevOverTime),
];
}
@ -175,8 +177,8 @@ function createAggregationOverTime(name: string): QueryBuilderOperationDef {
return {
id: name,
name: getPromAndLokiOperationDisplayName(name),
params: [getAggregationOverTimeRangeVector()],
defaultParams: ['auto'],
params: [getRangeVectorParamDef()],
defaultParams: ['$__interval'],
alternativesKey: 'overtime function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: operationWithRangeVectorRenderer,
@ -184,24 +186,11 @@ function createAggregationOverTime(name: string): QueryBuilderOperationDef {
};
}
function getAggregationOverTimeRangeVector(): QueryBuilderOperationParamDef {
return {
name: 'Range vector',
type: 'string',
options: ['auto', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'],
};
}
function operationWithRangeVectorRenderer(
model: QueryBuilderOperation,
def: QueryBuilderOperationDef,
innerExpr: string
) {
let rangeVector = (model.params ?? [])[0] ?? 'auto';
if (rangeVector === 'auto') {
rangeVector = '$__interval';
}
let rangeVector = (model.params ?? [])[0] ?? '$__interval';
return `${def.id}(${innerExpr}[${rangeVector}])`;
}

View File

@ -4,13 +4,13 @@ import {
functionRendererLeft,
functionRendererRight,
getPromAndLokiOperationDisplayName,
getRangeVectorParamDef,
rangeRendererLeftWithParams,
rangeRendererRightWithParams,
} from './shared/operationUtils';
import {
QueryBuilderOperation,
QueryBuilderOperationDef,
QueryBuilderOperationParamDef,
QueryWithOperations,
VisualQueryModeller,
} from './shared/types';
@ -51,10 +51,45 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] {
addOperationHandler: defaultAddOperationHandler,
},
createRangeFunction(PromOperationId.Changes),
createRangeFunction(PromOperationId.Rate),
createRangeFunction(PromOperationId.Rate, true),
createRangeFunction(PromOperationId.Irate),
createRangeFunction(PromOperationId.Increase),
createRangeFunction(PromOperationId.Increase, true),
createRangeFunction(PromOperationId.Idelta),
createRangeFunction(PromOperationId.Delta),
createFunction({
id: PromOperationId.HoltWinters,
params: [
getRangeVectorParamDef(),
{ name: 'Smoothing Factor', type: 'number' },
{ name: 'Trend Factor', type: 'number' },
],
defaultParams: ['$__interval', 0.5, 0.5],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
createFunction({
id: PromOperationId.PredictLinear,
params: [getRangeVectorParamDef(), { name: 'Seconds from now', type: 'number' }],
defaultParams: ['$__interval', 60],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
createFunction({
id: PromOperationId.QuantileOverTime,
params: [getRangeVectorParamDef(), { name: 'Quantile', type: 'number' }],
defaultParams: ['$__interval', 0.5],
alternativesKey: 'overtime function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererLeftWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
// Not sure about this one. It could also be a more generic 'Simple math operation' where user specifies
// both the operator and the operand in a single input
{
@ -85,7 +120,6 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] {
addOperationHandler: addNestedQueryHandler,
},
createFunction({ id: PromOperationId.Absent }),
createRangeFunction(PromOperationId.AbsentOverTime),
createFunction({
id: PromOperationId.Acos,
category: PromVisualQueryOperationCategory.Trigonometric,
@ -163,20 +197,7 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] {
createFunction({ id: PromOperationId.Exp }),
createFunction({ id: PromOperationId.Floor }),
createFunction({ id: PromOperationId.Group }),
createFunction({
id: PromOperationId.HoltWinters,
params: [
getRangeVectorParamDef(),
{ name: 'Smoothing Factor', type: 'number' },
{ name: 'Trend Factor', type: 'number' },
],
defaultParams: ['auto', 0.5, 0.5],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
}),
createFunction({ id: PromOperationId.Hour }),
createRangeFunction(PromOperationId.Idelta),
createFunction({
id: PromOperationId.LabelJoin,
params: [
@ -209,28 +230,12 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] {
id: PromOperationId.Pi,
renderer: (model) => `${model.id}()`,
}),
createFunction({
id: PromOperationId.PredictLinear,
params: [getRangeVectorParamDef(), { name: 'Seconds from now', type: 'number' }],
defaultParams: ['auto', 60],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
}),
createFunction({
id: PromOperationId.Quantile,
params: [{ name: 'Value', type: 'number' }],
defaultParams: [1],
renderer: functionRendererLeft,
}),
createFunction({
id: PromOperationId.QuantileOverTime,
params: [getRangeVectorParamDef(), { name: 'Quantile', type: 'number' }],
defaultParams: ['auto', 0.5],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererLeftWithParams,
}),
createFunction({ id: PromOperationId.Rad }),
createRangeFunction(PromOperationId.Resets),
createFunction({
@ -288,30 +293,40 @@ export function createFunction(definition: Partial<QueryBuilderOperationDef>): Q
};
}
export function createRangeFunction(name: string): QueryBuilderOperationDef {
export function createRangeFunction(name: string, withRateInterval = false): QueryBuilderOperationDef {
return {
id: name,
name: getPromAndLokiOperationDisplayName(name),
params: [getRangeVectorParamDef()],
defaultParams: ['auto'],
params: [getRangeVectorParamDef(withRateInterval)],
defaultParams: [withRateInterval ? '$__rate_interval' : '$__interval'],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: operationWithRangeVectorRenderer,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
};
}
function operationTypeChangedHandlerForRangeFunction(
operation: QueryBuilderOperation,
newDef: QueryBuilderOperationDef
) {
// validate current parameter
if (operation.params[0] === '$__rate_interval' && newDef.defaultParams[0] !== '$__rate_interval') {
operation.params = newDef.defaultParams;
} else if (operation.params[0] === '$__interval' && newDef.defaultParams[0] !== '$__interval') {
operation.params = newDef.defaultParams;
}
return operation;
}
export function operationWithRangeVectorRenderer(
model: QueryBuilderOperation,
def: QueryBuilderOperationDef,
innerExpr: string
) {
let rangeVector = (model.params ?? [])[0] ?? 'auto';
if (rangeVector === 'auto') {
rangeVector = '$__rate_interval';
}
let rangeVector = (model.params ?? [])[0] ?? '5m';
return `${def.id}(${innerExpr}[${rangeVector}])`;
}
@ -321,14 +336,6 @@ function getSimpleBinaryRenderer(operator: string) {
};
}
function getRangeVectorParamDef(): QueryBuilderOperationParamDef {
return {
name: 'Range vector',
type: 'string',
options: ['auto', '$__rate_interval', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'],
};
}
/**
* Since there can only be one operation with range vector this will replace the current one (if one was added )
*/
@ -337,28 +344,22 @@ export function addOperationWithRangeVector(
query: PromVisualQuery,
modeller: VisualQueryModeller
) {
const newOperation: QueryBuilderOperation = {
id: def.id,
params: def.defaultParams,
};
if (query.operations.length > 0) {
const firstOp = modeller.getOperationDef(query.operations[0].id);
if (firstOp.addOperationHandler === addOperationWithRangeVector) {
return {
...query,
operations: [
{
...query.operations[0],
id: def.id,
},
...query.operations.slice(1),
],
operations: [newOperation, ...query.operations.slice(1)],
};
}
}
const newOperation: QueryBuilderOperation = {
id: def.id,
params: def.defaultParams,
};
return {
...query,
operations: [newOperation, ...query.operations],

View File

@ -60,10 +60,9 @@ export const OperationName = React.memo<Props>(({ operation, def, index, onChang
onCloseMenu={onToggleSwitcher}
onChange={(value) => {
if (value.value) {
onChange(index, {
...operation,
id: value.value.id,
});
const newDef = queryModeller.getOperationDef(value.value.id);
let changedOp = { ...operation, id: value.value.id };
onChange(index, def.changeTypeHandler ? def.changeTypeHandler(changedOp, newDef) : changedOp);
}
}}
/>

View File

@ -1,4 +1,4 @@
import { toOption } from '@grafana/data';
import { SelectableValue, toOption } from '@grafana/data';
import { Input, Select } from '@grafana/ui';
import React, { ComponentType } from 'react';
import { QueryBuilderOperationParamDef, QueryBuilderOperationParamEditorProps } from '../shared/types';
@ -45,16 +45,22 @@ function SelectInputParamEditor({
operationIndex,
onChange,
}: QueryBuilderOperationParamEditorProps) {
const selectOptions = paramDef.options!.map((option) => ({
label: option as string,
value: option as string,
}));
let selectOptions = paramDef.options as Array<SelectableValue<any>>;
if (!selectOptions[0]?.label) {
selectOptions = paramDef.options!.map((option) => ({
label: option.toString(),
value: option as string,
}));
}
let valueOption = selectOptions.find((x) => x.value === value) ?? toOption(value as string);
return (
<Select
id={getOperationParamId(operationIndex, index)}
menuShouldPortal
value={toOption(value as string)}
value={valueOption}
options={selectOptions}
onChange={(value) => onChange(index, value.value!)}
/>

View File

@ -1,5 +1,10 @@
import { capitalize } from 'lodash';
import { QueryBuilderOperation, QueryBuilderOperationDef, QueryWithOperations } from './types';
import {
QueryBuilderOperation,
QueryBuilderOperationDef,
QueryBuilderOperationParamDef,
QueryWithOperations,
} from './types';
export function functionRendererLeft(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) {
const params = renderParams(model, def, innerExpr);
@ -33,12 +38,7 @@ function rangeRendererWithParams(
throw `Cannot render a function with params of length [${def.params.length}]`;
}
// First, make sure the first parameter (that is the range vector) is translated if the user selected 'auto'
let rangeVector = (model.params ?? [])[0] ?? 'auto';
if (rangeVector === 'auto') {
rangeVector = '$__rate_interval';
}
let rangeVector = (model.params ?? [])[0] ?? '5m';
// Next frame the remaining parameters, but get rid of the first one because it's used to move the
// instant vector into a range vector.
@ -114,3 +114,32 @@ export function getPromAndLokiOperationDisplayName(funcName: string) {
export function getOperationParamId(operationIndex: number, paramIndex: number) {
return `operations.${operationIndex}.param.${paramIndex}`;
}
export function getRangeVectorParamDef(withRateInterval = false): QueryBuilderOperationParamDef {
const param = {
name: 'Range',
type: 'string',
options: [
{
label: '$__interval',
value: '$__interval',
// tooltip: 'Dynamic interval based on max data points, scrape and min interval',
},
{ label: '1m', value: '1m' },
{ label: '5m', value: '5m' },
{ label: '10m', value: '10m' },
{ label: '1h', value: '1h' },
{ label: '24h', value: '24h' },
],
};
if (withRateInterval) {
param.options.unshift({
label: '$__rate_interval',
value: '$__rate_interval',
// tooltip: 'Always above 4x scrape interval',
});
}
return param;
}

View File

@ -31,6 +31,7 @@ export interface QueryBuilderOperationDef<T = any> extends RegistryItem {
addOperationHandler: QueryBuilderAddOperationHandler<T>;
paramChangedHandler?: QueryBuilderOnParamChangedHandler;
explainHandler?: (op: QueryBuilderOperation, def: QueryBuilderOperationDef<T>) => string;
changeTypeHandler?: (op: QueryBuilderOperation, newDef: QueryBuilderOperationDef<T>) => QueryBuilderOperation;
}
export type QueryBuilderAddOperationHandler<T> = (