Logs: Log samples not being ordered correctly (#64097)

* fix log samples being unsorted and multiple results when chunking

* use `SortDirection` enum

* changed to `sortDataFrame` to support other datasources than loki

* update tests

* change capitalization
This commit is contained in:
Sven Grossmann 2023-03-03 16:02:14 +01:00 committed by GitHub
parent d61bcdf4ca
commit 2076282064
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 20 deletions

View File

@ -15,6 +15,7 @@ import {
LogsMetaKind,
LogsVolumeType,
MutableDataFrame,
sortDataFrame,
toDataFrame,
} from '@grafana/data';
@ -1345,14 +1346,14 @@ describe('logs sample', () => {
const resultAFrame1 = createFrame([{ app: 'app01' }], [100, 200, 300], ['line 1', 'line 2', 'line 3']);
const resultAFrame2 = createFrame(
[{ app: 'app01', level: 'error' }],
[100, 200, 300],
[400, 500, 600],
['line 4', 'line 5', 'line 6']
);
const resultBFrame1 = createFrame([{ app: 'app02' }], [100, 200, 300], ['line A', 'line B', 'line C']);
const resultBFrame1 = createFrame([{ app: 'app02' }], [700, 800, 900], ['line A', 'line B', 'line C']);
const resultBFrame2 = createFrame(
[{ app: 'app02', level: 'error' }],
[100, 200, 300],
[1000, 1100, 1200],
['line D', 'line E', 'line F']
);
@ -1362,7 +1363,7 @@ describe('logs sample', () => {
data: [resultAFrame1, resultAFrame2],
},
{
data: [resultBFrame1, resultBFrame2],
data: [resultBFrame1, resultBFrame2, resultAFrame1, resultAFrame2],
},
]);
}
@ -1374,14 +1375,17 @@ describe('logs sample', () => {
it('returns data', async () => {
setup(setupMultipleResults);
await expect(logsSampleProvider).toEmitValuesWith((received) => {
expect(received).toMatchObject([
{ state: LoadingState.Loading, error: undefined, data: [] },
{
state: LoadingState.Done,
error: undefined,
data: [resultAFrame1, resultAFrame2, resultBFrame1, resultBFrame2],
},
]);
expect(received).toContainEqual({ state: LoadingState.Loading, error: undefined, data: [] });
expect(received).toContainEqual(
expect.objectContaining({
data: expect.arrayContaining([
sortDataFrame(resultAFrame1, 0),
sortDataFrame(resultAFrame2, 0),
sortDataFrame(resultBFrame1, 0),
sortDataFrame(resultBFrame2, 0),
]),
})
);
});
});

View File

@ -18,6 +18,7 @@ import {
FieldWithIndex,
findCommonLabels,
findUniqueLabels,
getTimeField,
Labels,
LoadingState,
LogLevel,
@ -30,6 +31,7 @@ import {
MutableDataFrame,
rangeUtil,
ScopedVars,
sortDataFrame,
textUtil,
TimeRange,
toDataFrame,
@ -41,6 +43,7 @@ import { ansicolor, colors } from '@grafana/ui';
import { getThemeColor } from 'app/core/utils/colors';
import { getLogLevel, getLogLevelFromKey, sortInAscendingOrder } from '../features/logs/utils';
export const LIMIT_LABEL = 'Line limit';
export const COMMON_LABELS = 'Common labels';
@ -798,7 +801,11 @@ export function queryLogsSample<TQuery extends DataQuery, TOptions extends DataS
});
observer.error(error);
} else {
rawLogsSample = rawLogsSample.concat(dataQueryResponse.data.map(toDataFrame));
rawLogsSample = dataQueryResponse.data.map((dataFrame) => {
const frame = toDataFrame(dataFrame);
const { timeIndex } = getTimeField(frame);
return sortDataFrame(frame, timeIndex);
});
}
},
error: (error) => {

View File

@ -78,7 +78,7 @@ import {
isValidQuery,
requestSupportsPartitioning,
} from './queryUtils';
import { sortDataFrameByTime } from './sortDataFrame';
import { sortDataFrameByTime, SortDirection } from './sortDataFrame';
import { doLokiChannelStream } from './streaming';
import { trackQuery } from './tracking';
import {
@ -686,7 +686,7 @@ export class LokiDatasource
const processResults = (result: DataQueryResponse): DataQueryResponse => {
const frames: DataFrame[] = result.data;
const processedFrames = frames
.map((frame) => sortDataFrameByTime(frame, 'DESCENDING'))
.map((frame) => sortDataFrameByTime(frame, SortDirection.Descending))
.map((frame) => processDataFrame(frame)); // rename fields if needed
return {

View File

@ -1,6 +1,6 @@
import { ArrayVector, DataFrame, FieldType } from '@grafana/data';
import { sortDataFrameByTime } from './sortDataFrame';
import { sortDataFrameByTime, SortDirection } from './sortDataFrame';
const inputFrame: DataFrame = {
refId: 'A',
@ -29,7 +29,7 @@ const inputFrame: DataFrame = {
describe('loki sortDataFrame', () => {
it('sorts a dataframe ascending', () => {
const sortedFrame = sortDataFrameByTime(inputFrame, 'ASCENDING');
const sortedFrame = sortDataFrameByTime(inputFrame, SortDirection.Ascending);
expect(sortedFrame.length).toBe(5);
const timeValues = sortedFrame.fields[0].values.toArray();
const lineValues = sortedFrame.fields[1].values.toArray();
@ -40,7 +40,7 @@ describe('loki sortDataFrame', () => {
expect(tsNsValues).toStrictEqual([`1001000000`, `1002000000`, `1003000000`, `1004000000`, `1005000000`]);
});
it('sorts a dataframe descending', () => {
const sortedFrame = sortDataFrameByTime(inputFrame, 'DESCENDING');
const sortedFrame = sortDataFrameByTime(inputFrame, SortDirection.Descending);
expect(sortedFrame.length).toBe(5);
const timeValues = sortedFrame.fields[0].values.toArray();
const lineValues = sortedFrame.fields[1].values.toArray();

View File

@ -1,6 +1,9 @@
import { DataFrame, Field, SortedVector } from '@grafana/data';
type SortDirection = 'ASCENDING' | 'DESCENDING';
export enum SortDirection {
Ascending,
Descending,
}
// creates the `index` for the sorting.
// this is needed by the `SortedVector`.
@ -21,7 +24,7 @@ function makeIndex(field: Field<string>, dir: SortDirection): number[] {
index[i] = i;
}
const isAsc = dir === 'ASCENDING';
const isAsc = dir === SortDirection.Ascending;
index.sort((a: number, b: number): number => {
// we need to answer this question: