From 05644e70423fafde5c3518ee99cd9f294a6466fe Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Fri, 30 Oct 2020 09:12:57 +0100 Subject: [PATCH] Loki: Fix error when some queries return zero results (#28645) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix logSeriesToLogsModel when some data frames have empty fields * Update test * Update public/app/core/logs_model.ts Co-authored-by: Zoltán Bedi Co-authored-by: Zoltán Bedi --- public/app/core/logs_model.test.ts | 72 ++++++++++++++++++++++++++++++ public/app/core/logs_model.ts | 13 +++--- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/public/app/core/logs_model.test.ts b/public/app/core/logs_model.test.ts index 7a6256b0f37..23bceee8ceb 100644 --- a/public/app/core/logs_model.test.ts +++ b/public/app/core/logs_model.test.ts @@ -700,6 +700,78 @@ describe('logSeriesToLogsModel', () => { expect(logSeriesToLogsModel(logSeries)).toMatchObject(metaData); }); + it('should return correct metaData when some data frames have empty fields', () => { + const logSeries: DataFrame[] = [ + toDataFrame({ + fields: [ + { + name: 'ts', + type: FieldType.time, + values: ['1970-01-01T00:00:01Z', '1970-02-01T00:00:01Z', '1970-03-01T00:00:01Z'], + }, + { + name: 'line', + type: FieldType.string, + values: ['WARN boooo 0', 'WARN boooo 1', 'WARN boooo 2'], + labels: { + foo: 'bar', + level: 'dbug', + }, + }, + { + name: 'id', + type: FieldType.string, + values: ['0', '1', '2'], + }, + ], + refId: 'A', + meta: { + searchWords: ['test'], + limit: 1000, + stats: [{ displayName: 'Summary: total bytes processed', value: 97048, unit: 'decbytes' }], + custom: { lokiQueryStatKey: 'Summary: total bytes processed' }, + preferredVisualisationType: 'logs', + }, + }), + toDataFrame({ + fields: [], + length: 0, + refId: 'B', + meta: { + searchWords: ['test'], + limit: 1000, + stats: [{ displayName: 'Summary: total bytes processed', value: 97048, unit: 'decbytes' }], + custom: { lokiQueryStatKey: 'Summary: total bytes processed' }, + preferredVisualisationType: 'logs', + }, + }), + ]; + + const logsModel = dataFrameToLogsModel(logSeries, 0, 'utc'); + expect(logsModel.meta).toMatchObject([ + { kind: 2, label: 'Common labels', value: { foo: 'bar', level: 'dbug' } }, + { kind: 1, label: 'Limit', value: '2000 (3 returned)' }, + { kind: 1, label: 'Total bytes processed', value: '194 kB' }, + ]); + expect(logsModel.rows).toHaveLength(3); + expect(logsModel.rows).toMatchObject([ + { + entry: 'WARN boooo 0', + labels: { foo: 'bar' }, + logLevel: LogLevel.debug, + }, + { + entry: 'WARN boooo 1', + labels: { foo: 'bar' }, + logLevel: LogLevel.debug, + }, + { + entry: 'WARN boooo 2', + labels: { foo: 'bar' }, + logLevel: LogLevel.debug, + }, + ]); + }); }); describe('getSeriesProperties()', () => { diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index 66d91e422db..3a42805a283 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -270,6 +270,7 @@ function separateLogsAndMetrics(dataFrames: DataFrame[]) { const logSeries: DataFrame[] = []; for (const dataFrame of dataFrames) { + // We want to show meta stats even if no result was returned. That's why we are pushing also data frames with no fields. if (isLogsData(dataFrame) || !dataFrame.fields.length) { logSeries.push(dataFrame); continue; @@ -306,8 +307,12 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi // Find the fields we care about and collect all labels let allSeries: LogFields[] = []; - if (hasFields(logSeries)) { - allSeries = logSeries.map(series => { + // We are sometimes passing data frames with no fields because we want to calculate correct meta stats. + // Therefore we need to filter out series with no fields. These series are used only for meta stats calculation. + const seriesWithFields = logSeries.filter(series => series.fields.length); + + if (seriesWithFields.length) { + allSeries = seriesWithFields.map(series => { const fieldCache = new FieldCache(series); const stringField = fieldCache.getFirstFieldOfType(FieldType.string); @@ -463,10 +468,6 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi }; } -function hasFields(logSeries: DataFrame[]): boolean { - return logSeries.some(series => series.fields.length); -} - function getIdField(fieldCache: FieldCache): FieldWithIndex | undefined { const idFieldNames = ['id']; for (const fieldName of idFieldNames) {