diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index 763d175548f..526da09a3eb 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -24,6 +24,7 @@ import { } from '@grafana/data'; import { getThemeColor } from 'app/core/utils/colors'; import { hasAnsiCodes } from 'app/core/utils/text'; +import { sortInAscendingOrder } from 'app/core/utils/explore'; import { getGraphSeriesModel } from 'app/plugins/panel/graph2/getGraphSeriesModel'; export const LogLevelColor = { @@ -106,7 +107,8 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Grap const bucketSize = intervalMs * 10; const seriesList: any[] = []; - for (const row of rows) { + const sortedRows = rows.sort(sortInAscendingOrder); + for (const row of sortedRows) { let series = seriesByLevel[row.logLevel]; if (!series) { @@ -120,8 +122,9 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Grap seriesList.push(series); } - // align time to bucket size - const time = Math.round(row.timeEpochMs / bucketSize) * bucketSize; + // align time to bucket size - used Math.floor for calculation as time of the bucket + // must be in the past (before Date.now()) to be displayed on the graph + const time = Math.floor(row.timeEpochMs / bucketSize) * bucketSize; // Entry for time if (time === series.lastTs) { diff --git a/public/app/core/specs/logs_model.test.ts b/public/app/core/specs/logs_model.test.ts index aa687d05ddf..1caeba49291 100644 --- a/public/app/core/specs/logs_model.test.ts +++ b/public/app/core/specs/logs_model.test.ts @@ -291,7 +291,7 @@ describe('dataFrameToLogsModel', () => { ]); }); - it('given multiple series should return expected logs model', () => { + it('given multiple series with unique times should return expected logs model', () => { const series: DataFrame[] = [ toDataFrame({ labels: { @@ -337,18 +337,18 @@ describe('dataFrameToLogsModel', () => { expect(logsModel.hasUniqueLabels).toBeTruthy(); expect(logsModel.rows).toHaveLength(3); expect(logsModel.rows).toMatchObject([ - { - entry: 'WARN boooo', - labels: { foo: 'bar', baz: '1' }, - logLevel: LogLevel.debug, - uniqueLabels: { baz: '1' }, - }, { entry: 'INFO 1', labels: { foo: 'bar', baz: '2' }, logLevel: LogLevel.error, uniqueLabels: { baz: '2' }, }, + { + entry: 'WARN boooo', + labels: { foo: 'bar', baz: '1' }, + logLevel: LogLevel.debug, + uniqueLabels: { baz: '1' }, + }, { entry: 'INFO 2', labels: { foo: 'bar', baz: '2' }, @@ -367,4 +367,96 @@ describe('dataFrameToLogsModel', () => { kind: LogsMetaKind.LabelsMap, }); }); + // + it('given multiple series with equal times should return expected logs model', () => { + const series: DataFrame[] = [ + toDataFrame({ + labels: { + foo: 'bar', + baz: '1', + level: 'dbug', + }, + fields: [ + { + name: 'ts', + type: FieldType.time, + values: ['1970-01-01T00:00:00Z'], + }, + { + name: 'line', + type: FieldType.string, + values: ['WARN boooo 1'], + }, + ], + }), + toDataFrame({ + labels: { + foo: 'bar', + baz: '2', + level: 'dbug', + }, + fields: [ + { + name: 'ts', + type: FieldType.time, + values: ['1970-01-01T00:00:01Z'], + }, + { + name: 'line', + type: FieldType.string, + values: ['WARN boooo 2'], + }, + ], + }), + toDataFrame({ + name: 'logs', + labels: { + foo: 'bar', + baz: '2', + level: 'err', + }, + fields: [ + { + name: 'time', + type: FieldType.time, + values: ['1970-01-01T00:00:00Z', '1970-01-01T00:00:01Z'], + }, + { + name: 'message', + type: FieldType.string, + values: ['INFO 1', 'INFO 2'], + }, + ], + }), + ]; + const logsModel = dataFrameToLogsModel(series, 0); + expect(logsModel.hasUniqueLabels).toBeTruthy(); + expect(logsModel.rows).toHaveLength(4); + expect(logsModel.rows).toMatchObject([ + { + entry: 'WARN boooo 1', + labels: { foo: 'bar', baz: '1' }, + logLevel: LogLevel.debug, + uniqueLabels: { baz: '1' }, + }, + { + entry: 'INFO 1', + labels: { foo: 'bar', baz: '2' }, + logLevel: LogLevel.error, + uniqueLabels: { baz: '2' }, + }, + { + entry: 'WARN boooo 2', + labels: { foo: 'bar', baz: '2' }, + logLevel: LogLevel.debug, + uniqueLabels: { baz: '2' }, + }, + { + entry: 'INFO 2', + labels: { foo: 'bar', baz: '2' }, + logLevel: LogLevel.error, + uniqueLabels: { baz: '2' }, + }, + ]); + }); }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 25f5ca982cd..534c9238db4 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -464,7 +464,7 @@ export const getRefIds = (value: any): string[] => { return _.uniq(_.flatten(refIds)); }; -const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => { +export const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => { if (a.timestamp < b.timestamp) { return -1; }