diff --git a/public/app/core/logsModel.test.ts b/public/app/core/logsModel.test.ts index f961a6d0630..aa4d74d1e41 100644 --- a/public/app/core/logsModel.test.ts +++ b/public/app/core/logsModel.test.ts @@ -14,6 +14,7 @@ import { LogsDedupStrategy, LogsMetaKind, MutableDataFrame, + sortDataFrame, toDataFrame, } from '@grafana/data'; @@ -1260,14 +1261,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'] ); @@ -1277,7 +1278,7 @@ describe('logs sample', () => { data: [resultAFrame1, resultAFrame2], }, { - data: [resultBFrame1, resultBFrame2], + data: [resultBFrame1, resultBFrame2, resultAFrame1, resultAFrame2], }, ]); } @@ -1289,14 +1290,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), + ]), + }) + ); }); }); diff --git a/public/app/core/logsModel.ts b/public/app/core/logsModel.ts index c3ea25df244..2754e98516d 100644 --- a/public/app/core/logsModel.ts +++ b/public/app/core/logsModel.ts @@ -18,6 +18,7 @@ import { FieldWithIndex, findCommonLabels, findUniqueLabels, + getTimeField, Labels, LoadingState, LogLevel, @@ -29,6 +30,7 @@ import { MutableDataFrame, rangeUtil, ScopedVars, + sortDataFrame, textUtil, TimeRange, toDataFrame, @@ -40,6 +42,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'; @@ -788,7 +791,11 @@ export function queryLogsSample { + const frame = toDataFrame(dataFrame); + const { timeIndex } = getTimeField(frame); + return sortDataFrame(frame, timeIndex); + }); } }, error: (error) => { diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 7e01a0ff6e3..88a8e9976bd 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -76,7 +76,7 @@ import { isLogsQuery, isValidQuery, } from './queryUtils'; -import { sortDataFrameByTime } from './sortDataFrame'; +import { sortDataFrameByTime, SortDirection } from './sortDataFrame'; import { doLokiChannelStream } from './streaming'; import { trackQuery } from './tracking'; import { @@ -677,7 +677,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 { diff --git a/public/app/plugins/datasource/loki/sortDataFrame.test.ts b/public/app/plugins/datasource/loki/sortDataFrame.test.ts index 165fc7cddfe..987c78ce3f3 100644 --- a/public/app/plugins/datasource/loki/sortDataFrame.test.ts +++ b/public/app/plugins/datasource/loki/sortDataFrame.test.ts @@ -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(); diff --git a/public/app/plugins/datasource/loki/sortDataFrame.ts b/public/app/plugins/datasource/loki/sortDataFrame.ts index c524de61160..dba69738950 100644 --- a/public/app/plugins/datasource/loki/sortDataFrame.ts +++ b/public/app/plugins/datasource/loki/sortDataFrame.ts @@ -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, 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: