diff --git a/public/app/plugins/datasource/loki/backendResultTransformer.test.ts b/public/app/plugins/datasource/loki/backendResultTransformer.test.ts index c159f07374e..897f9e6fb6e 100644 --- a/public/app/plugins/datasource/loki/backendResultTransformer.test.ts +++ b/public/app/plugins/datasource/loki/backendResultTransformer.test.ts @@ -186,4 +186,50 @@ describe('loki backendResultTransformer', () => { ); expect(result.data[0]?.meta?.custom?.error).toBe('Error when parsing some of the logs'); }); + + it('improve loki escaping error message when query contains escape', () => { + const response: DataQueryResponse = { + data: [], + error: { + refId: 'A', + message: 'parse error at line 1, col 2: invalid char escape', + }, + }; + + const result = transformBackendResult( + response, + [ + { + refId: 'A', + expr: '{place="g\\arden"}', + }, + ], + [] + ); + expect(result.error?.message).toBe( + `parse error at line 1, col 2: invalid char escape. Make sure that all special characters are escaped with \\. For more information on escaping of special characters visit LogQL documentation at https://grafana.com/docs/loki/latest/logql/.` + ); + }); + + it('do not change loki escaping error message when query does not contain escape', () => { + const response: DataQueryResponse = { + data: [], + error: { + refId: 'A', + message: 'parse error at line 1, col 2: invalid char escape', + }, + }; + + const result = transformBackendResult( + response, + [ + { + refId: 'A', + expr: '{place="garden"}', + }, + ], + [] + ); + expect(result.error?.message).toBe('parse error at line 1, col 2: invalid char escape'); + }); }); diff --git a/public/app/plugins/datasource/loki/backendResultTransformer.ts b/public/app/plugins/datasource/loki/backendResultTransformer.ts index 2154eeb19f1..e1a9d8674c1 100644 --- a/public/app/plugins/datasource/loki/backendResultTransformer.ts +++ b/public/app/plugins/datasource/loki/backendResultTransformer.ts @@ -1,4 +1,4 @@ -import { DataQueryResponse, DataFrame, isDataFrame, FieldType, QueryResultMeta } from '@grafana/data'; +import { DataQueryResponse, DataFrame, isDataFrame, FieldType, QueryResultMeta, DataQueryError } from '@grafana/data'; import { getDerivedFields } from './getDerivedFields'; import { makeTableFrames } from './makeTableFrames'; @@ -101,12 +101,39 @@ function groupFrames( return { streamsFrames, metricInstantFrames, metricRangeFrames }; } +function improveError(error: DataQueryError | undefined, queryMap: Map): DataQueryError | undefined { + // many things are optional in an error-object, we need an error-message to exist, + // and we need to find the loki-query, based on the refId in the error-object. + if (error === undefined) { + return error; + } + + const { refId, message } = error; + if (refId === undefined || message === undefined) { + return error; + } + + const query = queryMap.get(refId); + if (query === undefined) { + return error; + } + + if (message.includes('escape') && query.expr.includes('\\')) { + return { + ...error, + message: `${message}. Make sure that all special characters are escaped with \\. For more information on escaping of special characters visit LogQL documentation at https://grafana.com/docs/loki/latest/logql/.`, + }; + } + + return error; +} + export function transformBackendResult( response: DataQueryResponse, queries: LokiQuery[], derivedFieldConfigs: DerivedFieldConfig[] ): DataQueryResponse { - const { data, ...rest } = response; + const { data, error, ...rest } = response; // in the typescript type, data is an array of basically anything. // we do know that they have to be dataframes, so we make a quick check, @@ -124,6 +151,7 @@ export function transformBackendResult( return { ...rest, + error: improveError(error, queryMap), data: [ ...processMetricRangeFrames(metricRangeFrames), ...processMetricInstantFrames(metricInstantFrames),