mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
236 lines
5.6 KiB
TypeScript
236 lines
5.6 KiB
TypeScript
import { cloneDeep } from 'lodash';
|
|
|
|
import { ArrayVector, DataFrame, DataQueryResponse, Field, FieldType } from '@grafana/data';
|
|
|
|
import { transformBackendResult } from './backendResultTransformer';
|
|
|
|
// needed because the derived-fields functionality calls it
|
|
jest.mock('@grafana/runtime', () => ({
|
|
// @ts-ignore
|
|
...jest.requireActual('@grafana/runtime'),
|
|
getDataSourceSrv: () => {
|
|
return {
|
|
getInstanceSettings: () => {
|
|
return { name: 'Loki1' };
|
|
},
|
|
};
|
|
},
|
|
}));
|
|
|
|
const LOKI_EXPR = '{level="info"} |= "thing1"';
|
|
const inputFrame: DataFrame = {
|
|
refId: 'A',
|
|
meta: {
|
|
executedQueryString: LOKI_EXPR,
|
|
custom: {
|
|
frameType: 'LabeledTimeValues',
|
|
},
|
|
},
|
|
fields: [
|
|
{
|
|
name: 'Time',
|
|
type: FieldType.time,
|
|
config: {},
|
|
values: new ArrayVector([1645030244810, 1645030247027]),
|
|
},
|
|
{
|
|
name: 'Line',
|
|
type: FieldType.string,
|
|
config: {},
|
|
values: new ArrayVector(['line1', 'line2']),
|
|
},
|
|
{
|
|
name: 'labels',
|
|
type: FieldType.other,
|
|
config: {},
|
|
values: new ArrayVector([
|
|
{ level: 'info', code: '41🌙' },
|
|
{ level: 'error', code: '41🌙' },
|
|
]),
|
|
},
|
|
{
|
|
name: 'tsNs',
|
|
type: FieldType.string,
|
|
config: {},
|
|
values: new ArrayVector(['1645030244810757120', '1645030247027735040']),
|
|
},
|
|
{
|
|
name: 'id',
|
|
type: FieldType.string,
|
|
config: {},
|
|
values: new ArrayVector(['id1', 'id2']),
|
|
},
|
|
],
|
|
length: 5,
|
|
};
|
|
|
|
describe('loki backendResultTransformer', () => {
|
|
it('processes a logs-dataframe correctly', () => {
|
|
const response: DataQueryResponse = { data: [cloneDeep(inputFrame)] };
|
|
|
|
const expectedFrame = cloneDeep(inputFrame);
|
|
expectedFrame.meta = {
|
|
...expectedFrame.meta,
|
|
preferredVisualisationType: 'logs',
|
|
searchWords: ['thing1'],
|
|
custom: {
|
|
...expectedFrame.meta?.custom,
|
|
lokiQueryStatKey: 'Summary: total bytes processed',
|
|
},
|
|
};
|
|
|
|
const expected: DataQueryResponse = { data: [expectedFrame] };
|
|
|
|
const result = transformBackendResult(
|
|
response,
|
|
[
|
|
{
|
|
refId: 'A',
|
|
expr: LOKI_EXPR,
|
|
},
|
|
],
|
|
[]
|
|
);
|
|
expect(result).toEqual(expected);
|
|
});
|
|
|
|
it('applies maxLines correctly', () => {
|
|
const response: DataQueryResponse = { data: [cloneDeep(inputFrame)] };
|
|
|
|
const frame1: DataFrame = transformBackendResult(
|
|
response,
|
|
[
|
|
{
|
|
refId: 'A',
|
|
expr: LOKI_EXPR,
|
|
},
|
|
],
|
|
[]
|
|
).data[0];
|
|
|
|
expect(frame1.meta?.limit).toBeUndefined();
|
|
|
|
const frame2 = transformBackendResult(
|
|
response,
|
|
[
|
|
{
|
|
refId: 'A',
|
|
expr: LOKI_EXPR,
|
|
maxLines: 42,
|
|
},
|
|
],
|
|
[]
|
|
).data[0];
|
|
|
|
expect(frame2.meta?.limit).toBe(42);
|
|
});
|
|
|
|
it('processed derived fields correctly', () => {
|
|
const input: DataFrame = {
|
|
length: 1,
|
|
fields: [
|
|
{
|
|
name: 'time',
|
|
config: {},
|
|
values: new ArrayVector([1]),
|
|
type: FieldType.time,
|
|
},
|
|
{
|
|
name: 'line',
|
|
config: {},
|
|
values: new ArrayVector(['line1']),
|
|
type: FieldType.string,
|
|
},
|
|
],
|
|
};
|
|
const response: DataQueryResponse = { data: [input] };
|
|
const result = transformBackendResult(
|
|
response,
|
|
[{ refId: 'A', expr: '' }],
|
|
[
|
|
{
|
|
matcherRegex: 'trace=(w+)',
|
|
name: 'derived1',
|
|
url: 'example.com',
|
|
},
|
|
]
|
|
);
|
|
|
|
expect(
|
|
result.data[0].fields.filter((field: Field) => field.name === 'derived1' && field.type === 'string').length
|
|
).toBe(1);
|
|
});
|
|
|
|
it('handle loki parsing errors', () => {
|
|
const clonedFrame = cloneDeep(inputFrame);
|
|
clonedFrame.fields[2] = {
|
|
name: 'labels',
|
|
type: FieldType.string,
|
|
config: {},
|
|
values: new ArrayVector([
|
|
{ level: 'info', code: '41🌙', __error__: 'LogfmtParserErr' },
|
|
{ level: 'error', code: '41🌙' },
|
|
]),
|
|
};
|
|
const response: DataQueryResponse = { data: [clonedFrame] };
|
|
|
|
const result = transformBackendResult(
|
|
response,
|
|
[
|
|
{
|
|
refId: 'A',
|
|
expr: LOKI_EXPR,
|
|
},
|
|
],
|
|
[]
|
|
);
|
|
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');
|
|
});
|
|
});
|