Loki: Improve getLogQueryFromMetricsQuery (#75492)

* Query utils: fix bug in getLogQueryFromMetricsQuery function

* Query utils: implement getLogQueryFromMetricsQueryAtPosition

* Loki autocomplete situation: use getLogQueryFromMetricsQueryAtPosition

* Fix typo

* getLogQueryFromMetricsQueryAtPosition: use find instead of filter
This commit is contained in:
Matias Chomicki 2023-09-27 10:39:50 +02:00 committed by GitHub
parent d57aef2eda
commit a3d0dfcbcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 7 deletions

View File

@ -30,7 +30,7 @@ import {
LabelExtractionExpressionList,
} from '@grafana/lezer-logql';
import { getLogQueryFromMetricsQuery, getNodesFromQuery } from '../../../queryUtils';
import { getLogQueryFromMetricsQueryAtPosition, getNodesFromQuery } from '../../../queryUtils';
type Direction = 'parent' | 'firstChild' | 'lastChild' | 'nextSibling';
type NodeType = number;
@ -303,7 +303,7 @@ function getLabels(selectorNode: SyntaxNode, text: string): Label[] {
function resolveAfterUnwrap(node: SyntaxNode, text: string, pos: number): Situation | null {
return {
type: 'AFTER_UNWRAP',
logQuery: getLogQueryFromMetricsQuery(text).trim(),
logQuery: getLogQueryFromMetricsQueryAtPosition(text, pos).trim(),
};
}
@ -353,7 +353,7 @@ function resolveLabelsForGrouping(node: SyntaxNode, text: string, pos: number):
return {
type: 'IN_GROUPING',
logQuery: getLogQueryFromMetricsQuery(text).trim(),
logQuery: getLogQueryFromMetricsQueryAtPosition(text, pos).trim(),
};
}
@ -465,7 +465,7 @@ function resolveLogfmtParser(_: SyntaxNode, text: string, cursorPosition: number
type: 'IN_LOGFMT',
otherLabels,
flags,
logQuery: getLogQueryFromMetricsQuery(text).trim(),
logQuery: getLogQueryFromMetricsQueryAtPosition(text, position).trim(),
};
}
@ -539,7 +539,7 @@ function resolveLogOrLogRange(node: SyntaxNode, text: string, pos: number, after
type: 'AFTER_SELECTOR',
afterPipe,
hasSpace: text.charAt(pos - 1) === ' ',
logQuery: getLogQueryFromMetricsQuery(text).trim(),
logQuery: getLogQueryFromMetricsQueryAtPosition(text, pos).trim(),
};
}
@ -582,7 +582,7 @@ function resolveSelector(node: SyntaxNode, text: string, pos: number): Situation
}
function resolveAfterKeepAndDrop(node: SyntaxNode, text: string, pos: number): Situation | null {
let logQuery = getLogQueryFromMetricsQuery(text).trim();
let logQuery = getLogQueryFromMetricsQueryAtPosition(text, pos).trim();
let keepAndDropParent: SyntaxNode | null = null;
let parent = node.parent;
while (parent !== null) {

View File

@ -18,6 +18,7 @@ import {
getNormalizedLokiQuery,
getNodePositionsFromQuery,
formatLogqlQuery,
getLogQueryFromMetricsQueryAtPosition,
} from './queryUtils';
import { LokiQuery, LokiQueryType } from './types';
@ -427,6 +428,39 @@ describe('getLogQueryFromMetricsQuery', () => {
)
).toBe('{label="$var"} | logfmt | __error__=``');
});
it('does not return a query when there is no log query', () => {
expect(getLogQueryFromMetricsQuery('1+1')).toBe('');
expect(getLogQueryFromMetricsQuery('count_over_time([1s])')).toBe('');
});
});
describe('getLogQueryFromMetricsQueryAtPosition', () => {
it('works like getLogQueryFromMetricsQuery for simple queries', () => {
expect(
getLogQueryFromMetricsQueryAtPosition('count_over_time({job="grafana"} | logfmt | label="value" [1m])', 57)
).toBe('{job="grafana"} | logfmt | label="value"');
expect(getLogQueryFromMetricsQueryAtPosition('count_over_time({job="grafana"} [1m])', 37)).toBe('{job="grafana"}');
expect(
getLogQueryFromMetricsQueryAtPosition(
'sum(quantile_over_time(0.5, {label="$var"} | logfmt | __error__=`` | unwrap latency | __error__=`` [$__interval]))',
45
)
).toBe('{label="$var"} | logfmt | __error__=``');
});
it.each([
[
'count_over_time({place="moon"} | json test="test" [1m]) + avg_over_time({place="luna"} | logfmt test="test" [1m])',
'{place="moon"} | json test="test"',
49,
],
[
'count_over_time({place="moon"} | json test="test" [1m]) + avg_over_time({place="luna"} | logfmt test="test" [1m])',
'{place="luna"} | logfmt test="test"',
107,
],
])('gets the right query for complex queries', (metric: string, log: string, position: number) => {
expect(getLogQueryFromMetricsQueryAtPosition(metric, position)).toBe(log);
});
});
describe('getNodePositionsFromQuery', () => {

View File

@ -231,7 +231,7 @@ export function getLogQueryFromMetricsQuery(query: string): string {
// Log query in metrics query composes of Selector & PipelineExpr
const selectorNode = getNodeFromQuery(query, Selector);
if (!selectorNode) {
return query;
return '';
}
const selector = query.substring(selectorNode.from, selectorNode.to);
@ -241,6 +241,20 @@ export function getLogQueryFromMetricsQuery(query: string): string {
return `${selector} ${pipelineExpr}`.trim();
}
export function getLogQueryFromMetricsQueryAtPosition(query: string, position: number): string {
if (isLogsQuery(query)) {
return query;
}
const metricQuery = getNodesFromQuery(query, [MetricExpr])
.reverse() // So we don't get the root metric node
.find((node) => node.from <= position && node.to >= position);
if (!metricQuery) {
return '';
}
return getLogQueryFromMetricsQuery(query.substring(metricQuery.from, metricQuery.to));
}
export function isQueryWithLabelFilter(query: string): boolean {
return isQueryWithNode(query, LabelFilter);
}