D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
grafana
/
public
/
app
/
plugins
/
datasource
/
loki
/
querybuilder
/
Filename :
operations.ts
back
Copy
import { createAggregationOperation, createAggregationOperationWithParam, } from '../../prometheus/querybuilder/shared/operationUtils'; import { QueryBuilderOperationDef, QueryBuilderOperationParamValue } from '../../prometheus/querybuilder/shared/types'; import { binaryScalarOperations } from './binaryScalarOperations'; import { UnwrapParamEditor } from './components/UnwrapParamEditor'; import { addLokiOperation, addNestedQueryHandler, createRangeOperation, createRangeOperationWithGrouping, getLineFilterRenderer, labelFilterRenderer, pipelineRenderer, } from './operationUtils'; import { LokiOperationId, LokiOperationOrder, lokiOperators, LokiVisualQueryOperationCategory } from './types'; export function getOperationDefinitions(): QueryBuilderOperationDef[] { const aggregations = [ LokiOperationId.Sum, LokiOperationId.Min, LokiOperationId.Max, LokiOperationId.Avg, LokiOperationId.Stddev, LokiOperationId.Stdvar, LokiOperationId.Count, ].flatMap((opId) => createAggregationOperation(opId, { addOperationHandler: addLokiOperation, orderRank: LokiOperationOrder.Last, }) ); const aggregationsWithParam = [LokiOperationId.TopK, LokiOperationId.BottomK].flatMap((opId) => { return createAggregationOperationWithParam( opId, { params: [{ name: 'K-value', type: 'number' }], defaultParams: [5], }, { addOperationHandler: addLokiOperation, orderRank: LokiOperationOrder.Last, } ); }); const rangeOperations = [ createRangeOperation(LokiOperationId.Rate), createRangeOperation(LokiOperationId.RateCounter), createRangeOperation(LokiOperationId.CountOverTime), createRangeOperation(LokiOperationId.SumOverTime), createRangeOperation(LokiOperationId.BytesRate), createRangeOperation(LokiOperationId.BytesOverTime), createRangeOperation(LokiOperationId.AbsentOverTime), ]; const rangeOperationsWithGrouping = [ ...createRangeOperationWithGrouping(LokiOperationId.AvgOverTime), ...createRangeOperationWithGrouping(LokiOperationId.MaxOverTime), ...createRangeOperationWithGrouping(LokiOperationId.MinOverTime), ...createRangeOperationWithGrouping(LokiOperationId.FirstOverTime), ...createRangeOperationWithGrouping(LokiOperationId.LastOverTime), ...createRangeOperationWithGrouping(LokiOperationId.StdvarOverTime), ...createRangeOperationWithGrouping(LokiOperationId.StddevOverTime), ...createRangeOperationWithGrouping(LokiOperationId.QuantileOverTime), ]; const list: QueryBuilderOperationDef[] = [ ...aggregations, ...aggregationsWithParam, ...rangeOperations, ...rangeOperationsWithGrouping, { id: LokiOperationId.Json, name: 'Json', params: [ { name: 'Expression', type: 'string', restParam: true, optional: true, minWidth: 18, placeholder: 'server="servers[0]"', description: 'Using expressions with your json parser will extract only the specified json fields to labels. You can specify one or more expressions in this way. All expressions must be quoted.', }, ], defaultParams: [], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Parsers, renderer: (model, def, innerExpr) => `${innerExpr} | json ${model.params.join(', ')}`.trim(), addOperationHandler: addLokiOperation, explainHandler: () => `This will extract keys and values from a [json](https://grafana.com/docs/loki/latest/logql/log_queries/#json) formatted log line as labels. The extracted labels can be used in label filter expressions and used as values for a range aggregation via the unwrap operation.`, }, { id: LokiOperationId.Logfmt, name: 'Logfmt', params: [], defaultParams: [], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Parsers, renderer: pipelineRenderer, addOperationHandler: addLokiOperation, explainHandler: () => `This will extract all keys and values from a [logfmt](https://grafana.com/docs/loki/latest/logql/log_queries/#logfmt) formatted log line as labels. The extracted labels can be used in label filter expressions and used as values for a range aggregation via the unwrap operation.`, }, { id: LokiOperationId.Regexp, name: 'Regexp', params: [ { name: 'String', type: 'string', hideName: true, placeholder: '<re>', description: 'The regexp expression that matches the structure of a log line.', minWidth: 20, }, ], defaultParams: [''], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Parsers, renderer: (model, def, innerExpr) => `${innerExpr} | regexp \`${model.params[0]}\``, addOperationHandler: addLokiOperation, explainHandler: () => `The [regexp parser](https://grafana.com/docs/loki/latest/logql/log_queries/#regular-expression) takes a single parameter | regexp "<re>" which is the regular expression using the Golang RE2 syntax. The regular expression must contain a least one named sub-match (e.g (?P<name>re)), each sub-match will extract a different label. The expression matches the structure of a log line. The extracted labels can be used in label filter expressions and used as values for a range aggregation via the unwrap operation.`, }, { id: LokiOperationId.Pattern, name: 'Pattern', params: [ { name: 'String', type: 'string', hideName: true, placeholder: '<pattern-expression>', description: 'The expression that matches the structure of a log line.', minWidth: 20, }, ], defaultParams: [''], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Parsers, renderer: (model, def, innerExpr) => `${innerExpr} | pattern \`${model.params[0]}\``, addOperationHandler: addLokiOperation, explainHandler: () => `The [pattern parser](https://grafana.com/docs/loki/latest/logql/log_queries/#pattern) allows the explicit extraction of fields from log lines by defining a pattern expression (| pattern \`<pattern-expression>\`). The expression matches the structure of a log line. The extracted labels can be used in label filter expressions and used as values for a range aggregation via the unwrap operation.`, }, { id: LokiOperationId.Unpack, name: 'Unpack', params: [], defaultParams: [], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Parsers, renderer: pipelineRenderer, addOperationHandler: addLokiOperation, explainHandler: () => `This will extract all keys and values from a JSON log line, [unpacking](https://grafana.com/docs/loki/latest/logql/log_queries/#unpack) all embedded labels in the pack stage. The extracted labels can be used in label filter expressions and used as values for a range aggregation via the unwrap operation.`, }, { id: LokiOperationId.LineFormat, name: 'Line format', params: [ { name: 'String', type: 'string', hideName: true, placeholder: '{{.status_code}}', description: 'A line template that can refer to stream labels and extracted labels.', minWidth: 20, }, ], defaultParams: [''], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.PipeOperations, renderer: (model, def, innerExpr) => `${innerExpr} | line_format \`${model.params[0]}\``, addOperationHandler: addLokiOperation, explainHandler: () => `This will replace log line using a specified template. The template can refer to stream labels and extracted labels. Example: \`{{.status_code}} - {{.message}}\` [Read the docs](https://grafana.com/docs/loki/latest/logql/log_queries/#line-format-expression) for more. `, }, { id: LokiOperationId.LabelFormat, name: 'Label format', params: [ { name: 'Label', type: 'string' }, { name: 'Rename to', type: 'string' }, ], defaultParams: ['', ''], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.PipeOperations, renderer: (model, def, innerExpr) => `${innerExpr} | label_format ${model.params[1]}=${model.params[0]}`, addOperationHandler: addLokiOperation, explainHandler: () => `This will change name of label to desired new label. In the example below, label "error_level" will be renamed to "level". Example: \`\`error_level=\`level\` \`\` [Read the docs](https://grafana.com/docs/loki/latest/logql/log_queries/#labels-format-expression) for more. `, }, { id: LokiOperationId.LineContains, name: 'Line contains', params: [ { name: 'String', type: 'string', hideName: true, placeholder: 'Text to find', description: 'Find log lines that contains this text', minWidth: 20, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('|='), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that contain string \`${op.params[0]}\`.`, }, { id: LokiOperationId.LineContainsNot, name: 'Line does not contain', params: [ { name: 'String', type: 'string', hideName: true, placeholder: 'Text to exclude', description: 'Find log lines that does not contain this text', minWidth: 26, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('!='), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that does not contain string \`${op.params[0]}\`.`, }, { id: LokiOperationId.LineContainsCaseInsensitive, name: 'Line contains case insensitive', params: [ { name: 'String', type: 'string', hideName: true, placeholder: 'Text to find', description: 'Find log lines that contains this text', minWidth: 33, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('|~', true), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that match regex \`(?i)${op.params[0]}\`.`, }, { id: LokiOperationId.LineContainsNotCaseInsensitive, name: 'Line does not contain case insensitive', params: [ { name: 'String', type: 'string', hideName: true, placeholder: 'Text to exclude', description: 'Find log lines that does not contain this text', minWidth: 40, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('!~', true), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that does not match regex \`(?i)${op.params[0]}\`.`, }, { id: LokiOperationId.LineMatchesRegex, name: 'Line contains regex match', params: [ { name: 'Regex', type: 'string', hideName: true, placeholder: 'Pattern to match', description: 'Find log lines that match this regex pattern', minWidth: 30, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('|~'), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that match a \`RE2\` regex pattern. \`${op.params[0]}\`.`, }, { id: LokiOperationId.LineMatchesRegexNot, name: 'Line does not match regex', params: [ { name: 'Regex', type: 'string', hideName: true, placeholder: 'Pattern to exclude', description: 'Find log lines that does not match this regex pattern', minWidth: 30, runQueryOnEnter: true, }, ], defaultParams: [''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: getLineFilterRenderer('!~'), addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines that doesn't match a \`RE2\` regex pattern. \`${op.params[0]}\`.`, }, { id: LokiOperationId.LineFilterIpMatches, name: 'IP line filter expression', params: [ { name: 'Operator', type: 'string', minWidth: 16, options: [lokiOperators.contains, lokiOperators.doesNotContain], }, { name: 'Pattern', type: 'string', placeholder: '<pattern>', minWidth: 16, runQueryOnEnter: true, }, ], defaultParams: ['|=', ''], alternativesKey: 'line filter', category: LokiVisualQueryOperationCategory.LineFilters, orderRank: LokiOperationOrder.LineFilters, renderer: (op, def, innerExpr) => `${innerExpr} ${op.params[0]} ip(\`${op.params[1]}\`)`, addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines using IP matching of \`${op.params[1]}\``, }, { id: LokiOperationId.LabelFilter, name: 'Label filter expression', params: [ { name: 'Label', type: 'string', minWidth: 14 }, { name: 'Operator', type: 'string', minWidth: 14, options: [ lokiOperators.equals, lokiOperators.doesNotEqual, lokiOperators.matchesRegex, lokiOperators.doesNotMatchRegex, lokiOperators.greaterThan, lokiOperators.lessThan, lokiOperators.greaterThanOrEqual, lokiOperators.lessThanOrEqual, ], }, { name: 'Value', type: 'string', minWidth: 14 }, ], defaultParams: ['', '=', ''], alternativesKey: 'label filter', category: LokiVisualQueryOperationCategory.LabelFilters, orderRank: LokiOperationOrder.PipeOperations, renderer: labelFilterRenderer, addOperationHandler: addLokiOperation, explainHandler: () => `Label expression filter allows filtering using original and extracted labels.`, }, { id: LokiOperationId.LabelFilterIpMatches, name: 'IP label filter expression', params: [ { name: 'Label', type: 'string', minWidth: 14 }, { name: 'Operator', type: 'string', minWidth: 14, options: [lokiOperators.equals, lokiOperators.doesNotEqual], }, { name: 'Value', type: 'string', minWidth: 14 }, ], defaultParams: ['', '=', ''], alternativesKey: 'label filter', category: LokiVisualQueryOperationCategory.LabelFilters, orderRank: LokiOperationOrder.PipeOperations, renderer: (model, def, innerExpr) => `${innerExpr} | ${model.params[0]} ${model.params[1]} ip(\`${model.params[2]}\`)`, addOperationHandler: addLokiOperation, explainHandler: (op) => `Return log lines using IP matching of \`${op.params[2]}\` for \`${op.params[0]}\` label`, }, { id: LokiOperationId.LabelFilterNoErrors, name: 'No pipeline errors', params: [], defaultParams: [], alternativesKey: 'label filter', category: LokiVisualQueryOperationCategory.LabelFilters, orderRank: LokiOperationOrder.NoErrors, renderer: (model, def, innerExpr) => `${innerExpr} | __error__=\`\``, addOperationHandler: addLokiOperation, explainHandler: () => `Filter out all formatting and parsing errors.`, }, { id: LokiOperationId.Unwrap, name: 'Unwrap', params: [ { name: 'Identifier', type: 'string', hideName: true, minWidth: 16, placeholder: 'Label key', editor: UnwrapParamEditor, }, { name: 'Conversion function', hideName: true, type: 'string', options: ['duration', 'duration_seconds', 'bytes'], optional: true, }, ], defaultParams: ['', ''], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.Unwrap, renderer: (op, def, innerExpr) => `${innerExpr} | unwrap ${op.params[1] ? `${op.params[1]}(${op.params[0]})` : op.params[0]}`, addOperationHandler: addLokiOperation, explainHandler: (op) => { let label = String(op.params[0]).length > 0 ? op.params[0] : '<label>'; return `Use the extracted label \`${label}\` as sample values instead of log lines for the subsequent range aggregation.${ op.params[1] ? ` Conversion function \`${op.params[1]}\` wrapping \`${label}\` will attempt to convert this label from a specific format (e.g. 3k, 500ms).` : '' }`; }, }, { id: LokiOperationId.Decolorize, name: 'Decolorize', params: [], defaultParams: [], alternativesKey: 'format', category: LokiVisualQueryOperationCategory.Formats, orderRank: LokiOperationOrder.PipeOperations, renderer: (op, def, innerExpr) => `${innerExpr} | decolorize`, addOperationHandler: addLokiOperation, explainHandler: () => `This will remove ANSI color codes from log lines.`, }, ...binaryScalarOperations, { id: LokiOperationId.NestedQuery, name: 'Binary operation with query', params: [], defaultParams: [], category: LokiVisualQueryOperationCategory.BinaryOps, renderer: (model, def, innerExpr) => innerExpr, addOperationHandler: addNestedQueryHandler, }, ]; return list; } // Keeping a local copy as an optimization measure. const definitions = getOperationDefinitions(); /** * Given an operator, return the corresponding explain. * For usage within the Query Editor. */ export function explainOperator(id: LokiOperationId | string): string { const definition = definitions.find((operation) => operation.id === id); const explain = definition?.explainHandler?.({ id: '', params: ['<value>'] }) || ''; // Strip markdown links return explain.replace(/\[(.*)\]\(.*\)/g, '$1'); } export function getDefinitionById(id: string): QueryBuilderOperationDef | undefined { return definitions.find((x) => x.id === id); } export function checkParamsAreValid(def: QueryBuilderOperationDef, params: QueryBuilderOperationParamValue[]): boolean { // For now we only check if the operation has all the required params. if (params.length < def.params.filter((param) => !param.optional).length) { return false; } return true; }