123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- 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: 'value',
- type: FieldType.string,
- config: {},
- values: new ArrayVector(['line1', 'line2']),
- },
- {
- name: 'labels',
- type: FieldType.string,
- 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');
- });
- });
|