Loki: Higlight search words in queries when using template variables (#91949)

* backendResultTransformer: use templateSrv to highlight search words from variables

* Add unit tests

* Formatting

* Fix import issues

* Update public/app/plugins/datasource/loki/backendResultTransformer.ts

Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>

* Refactor: use executedQueryString to get the interpolated query

* Remove unused imports

* Update public/app/plugins/datasource/loki/backendResultTransformer.test.ts

Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>

---------

Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>
This commit is contained in:
Matias Chomicki
2024-08-15 19:20:48 +03:00
committed by GitHub
co-authored by Sven Grossmann
parent c6ae2d7999
commit 4751bf915a
3 changed files with 41 additions and 7 deletions
@@ -64,8 +64,8 @@ const inputFrame: DataFrame = {
length: 5,
};
describe('loki backendResultTransformer', () => {
it('processes a logs-dataframe correctly', () => {
describe('backendResultTransformer', () => {
it('processes a logs dataframe correctly', () => {
const response: DataQueryResponse = { data: [cloneDeep(inputFrame)] };
const expectedFrame = cloneDeep(inputFrame);
@@ -232,4 +232,24 @@ describe('loki backendResultTransformer', () => {
);
expect(result.error?.message).toBe('parse error at line 1, col 2: invalid char escape');
});
it('resolves search words from queries with template variables', () => {
const dataFrame = cloneDeep(inputFrame);
dataFrame.meta = {
executedQueryString: 'Expr: {service_name="tns-app"} |~ "(?i)template" |= "variable"',
};
const result = transformBackendResult(
{ data: [dataFrame] },
[
{
refId: 'A',
expr: `{service_name="tns-app"} |~ "(?i)$search"`,
},
],
[]
);
expect(result.data[0].meta.searchWords).toContain('(?i)template');
expect(result.data[0].meta.searchWords).toContain('variable');
});
});
@@ -2,7 +2,7 @@ import { DataQueryResponse, DataFrame, isDataFrame, FieldType, QueryResultMeta,
import { getDerivedFields } from './getDerivedFields';
import { makeTableFrames } from './makeTableFrames';
import { getHighlighterExpressionsFromQuery } from './queryUtils';
import { getExpressionFromExecutedQuery, getHighlighterExpressionsFromQuery } from './queryUtils';
import { dataFrameHasLokiError } from './responseUtils';
import { DerivedFieldConfig, LokiQuery, LokiQueryType } from './types';
@@ -39,7 +39,7 @@ function processStreamFrame(
const meta: QueryResultMeta = {
preferredVisualisationType: 'logs',
limit: query?.maxLines,
searchWords: query !== undefined ? getHighlighterExpressionsFromQuery(query.expr) : undefined,
searchWords: query ? getHighlighterExpressionsFromQuery(query.expr) : undefined,
custom,
};
@@ -145,7 +145,17 @@ export function transformBackendResult(
return d;
});
const queryMap = new Map(queries.map((query) => [query.refId, query]));
const queryMap = new Map(
queries.map((query) => {
const executedExpr = response.data.find((data) => data.refId === query.refId)?.meta.executedQueryString;
const executedQuery = {
...query,
expr: executedExpr ? getExpressionFromExecutedQuery(executedExpr) : query.expr,
};
return [query.refId, executedQuery];
})
);
const { streamsFrames, metricInstantFrames, metricRangeFrames } = groupFrames(dataFrames, queryMap);
@@ -31,10 +31,10 @@ import { LokiQuery, LokiQueryType } from './types';
/**
* Returns search terms from a LogQL query.
* E.g., `{} |= foo |=bar != baz` returns `['foo', 'bar']`.
* E.g., `{} |= "foo" |= "bar" != "baz"` returns `['foo', 'bar']`.
*/
export function getHighlighterExpressionsFromQuery(input = ''): string[] {
const results = [];
const results: string[] = [];
const filters = getNodesFromQuery(input, [LineFilter]);
@@ -76,6 +76,10 @@ export function getHighlighterExpressionsFromQuery(input = ''): string[] {
return results;
}
export function getExpressionFromExecutedQuery(executedQueryString: string) {
return executedQueryString.replace('Expr: ', '');
}
export function getStringsFromLineFilter(filter: SyntaxNode): SyntaxNode[] {
const nodes: SyntaxNode[] = [];
let node: SyntaxNode | null = filter;