From 82c125d450bffaccbba4a7dc03ef4e771a9d9ce2 Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Fri, 16 Jun 2023 18:08:29 +0200 Subject: [PATCH] Loki: Implement step editor (#69648) * Loki: Implement step editor * Update to keep value * Remove console.log * Remove white space * Update public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx Co-authored-by: Matias Chomicki * Import trim * Update using of step in split queries * Add tests * Add tests * Remove step interpolation --------- Co-authored-by: Matias Chomicki --- pkg/tsdb/loki/parse_query.go | 5 +- pkg/tsdb/loki/step.go | 21 ++- pkg/tsdb/loki/step_test.go | 131 ++++++++++---- .../datasource/loki/querySplitting.test.ts | 162 ++++++++++++++++-- .../plugins/datasource/loki/querySplitting.ts | 54 +++--- .../LokiQueryBuilderOptions.test.tsx | 23 ++- .../components/LokiQueryBuilderOptions.tsx | 73 +++++--- .../plugins/datasource/loki/tracking.test.ts | 4 +- 8 files changed, 359 insertions(+), 114 deletions(-) diff --git a/pkg/tsdb/loki/parse_query.go b/pkg/tsdb/loki/parse_query.go index f510f0c43bb..e212e43568a 100644 --- a/pkg/tsdb/loki/parse_query.go +++ b/pkg/tsdb/loki/parse_query.go @@ -126,7 +126,10 @@ func parseQuery(queryContext *backend.QueryDataRequest) ([]*lokiQuery, error) { interval := query.Interval timeRange := query.TimeRange.To.Sub(query.TimeRange.From) - step := calculateStep(interval, timeRange, resolution) + step, err := calculateStep(interval, timeRange, resolution, model.Step) + if err != nil { + return nil, err + } expr := interpolateVariables(model.Expr, interval, timeRange) diff --git a/pkg/tsdb/loki/step.go b/pkg/tsdb/loki/step.go index 78f5d651d79..df66ebef466 100644 --- a/pkg/tsdb/loki/step.go +++ b/pkg/tsdb/loki/step.go @@ -3,6 +3,8 @@ package loki import ( "math" "time" + + "github.com/grafana/grafana/pkg/tsdb/intervalv2" ) // round the duration to the nearest millisecond larger-or-equal-to the duration @@ -20,12 +22,19 @@ func durationMax(d1 time.Duration, d2 time.Duration) time.Duration { } } -func calculateStep(baseInterval time.Duration, timeRange time.Duration, resolution int64) time.Duration { - step := time.Duration(baseInterval.Nanoseconds() * resolution) +func calculateStep(interval time.Duration, timeRange time.Duration, resolution int64, queryStep *string) (time.Duration, error) { + // If we don't have step from query we calculate it from interval, time range and resolution + if queryStep == nil || *queryStep == "" { + step := time.Duration(interval.Nanoseconds() * resolution) + safeStep := timeRange / 11000 + chosenStep := durationMax(step, safeStep) + return ceilMs(chosenStep), nil + } - safeStep := timeRange / 11000 + step, err := intervalv2.ParseIntervalStringToTimeDuration(*queryStep) + if err != nil { + return step, err + } - chosenStep := durationMax(step, safeStep) - - return ceilMs(chosenStep) + return time.Duration(step.Nanoseconds() * resolution), nil } diff --git a/pkg/tsdb/loki/step_test.go b/pkg/tsdb/loki/step_test.go index ad85d513879..91200d6720d 100644 --- a/pkg/tsdb/loki/step_test.go +++ b/pkg/tsdb/loki/step_test.go @@ -8,50 +8,111 @@ import ( ) func TestLokiStep(t *testing.T) { - t.Run("base case", func(t *testing.T) { - require.Equal(t, time.Second*14, calculateStep(time.Second*7, time.Second, 2)) - }) + t.Run("with query step", func(t *testing.T) { + t.Run("valid step in go duration format", func(t *testing.T) { + queryStep := "1m" + step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep) + require.NoError(t, err) + require.Equal(t, time.Minute*2, step) + }) - t.Run("step should be at least 1 millisecond", func(t *testing.T) { - require.Equal(t, time.Millisecond*1, calculateStep(time.Microsecond*500, time.Second, 1)) - }) + t.Run("valid step as number", func(t *testing.T) { + queryStep := "30" + step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep) + require.NoError(t, err) + require.Equal(t, time.Minute*1, step) + }) - t.Run("safeInterval should happen", func(t *testing.T) { - // safeInterval - require.Equal(t, time.Second*3, calculateStep(time.Second*2, time.Second*33000, 1)) - }) + // calculateStep parses a duration with support for unit that Grafana uses (e.g 1d) + t.Run("step with 1d", func(t *testing.T) { + queryStep := "1d" + step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep) + require.NoError(t, err) + require.Equal(t, time.Hour*48, step) + }) - t.Run("step should math.Ceil in milliseconds", func(t *testing.T) { - require.Equal(t, time.Millisecond*2, calculateStep(time.Microsecond*1234, time.Second*1, 1)) - }) + // calculateStep parses a duration with support for unit that Grafana uses (e.g 1w) + t.Run("step with 1w", func(t *testing.T) { + queryStep := "1w" + step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep) + require.NoError(t, err) + require.Equal(t, time.Hour*336, step) + }) - t.Run("step should math.Ceil in milliseconds, even if safeInterval happens", func(t *testing.T) { - require.Equal(t, time.Millisecond*3001, calculateStep(time.Second*2, time.Second*33001, 1)) + // Returns error + t.Run("invalid step", func(t *testing.T) { + queryStep := "invalid" + step, err := calculateStep(time.Second*7, time.Second, 2, &queryStep) + require.Error(t, err) + require.Equal(t, time.Duration(0), step) + }) }) + t.Run("with no query step", func(t *testing.T) { + t.Run("base case", func(t *testing.T) { + step, err := calculateStep(time.Second*7, time.Second, 2, nil) + require.NoError(t, err) + require.Equal(t, time.Second*14, step) + }) - t.Run("resolution should happen", func(t *testing.T) { - require.Equal(t, time.Second*5, calculateStep(time.Second*1, time.Second*100, 5)) - }) + t.Run("step should be at least 1 millisecond", func(t *testing.T) { + step, err := calculateStep(time.Microsecond*500, time.Second, 1, nil) + require.NoError(t, err) + require.Equal(t, time.Millisecond*1, step) + }) - t.Run("safeInterval check should happen after resolution is used", func(t *testing.T) { - require.Equal(t, time.Second*4, calculateStep(time.Second*2, time.Second*33000, 2)) - }) + t.Run("safeInterval should happen", func(t *testing.T) { + // safeInterval + step, err := calculateStep(time.Second*2, time.Second*33000, 1, nil) + require.NoError(t, err) + require.Equal(t, time.Second*3, step) + }) - t.Run("survive interval=0", func(t *testing.T) { - // interval=0. this should never happen, but we make sure we return something sane - // (in this case safeInterval will take care of the problem) - require.Equal(t, time.Second*2, calculateStep(time.Second*0, time.Second*22000, 1)) - }) + t.Run("step should math.Ceil in milliseconds", func(t *testing.T) { + step, err := calculateStep(time.Microsecond*1234, time.Second*1, 1, nil) + require.NoError(t, err) + require.Equal(t, time.Millisecond*2, step) + }) - t.Run("survive resolution=0", func(t *testing.T) { - // resolution=0. this should never happen, but we make sure we return something sane - // (in this case safeInterval will take care of the problem) - require.Equal(t, time.Second*2, calculateStep(time.Second*1, time.Second*22000, 0)) - }) + t.Run("step should math.Ceil in milliseconds, even if safeInterval happens", func(t *testing.T) { + step, err := calculateStep(time.Second*2, time.Second*33001, 1, nil) + require.NoError(t, err) + require.Equal(t, time.Millisecond*3001, step) + }) - t.Run("survive interval=0 and resolution=0", func(t *testing.T) { - // resolution=0 and interval=0. this should never happen, but we make sure we return something sane - // (in this case safeInterval will take care of the problem) - require.Equal(t, time.Second*2, calculateStep(time.Second*0, time.Second*22000, 0)) + t.Run("resolution should happen", func(t *testing.T) { + step, err := calculateStep(time.Second*1, time.Second*100, 5, nil) + require.NoError(t, err) + require.Equal(t, time.Second*5, step) + }) + + t.Run("safeInterval check should happen after resolution is used", func(t *testing.T) { + step, err := calculateStep(time.Second*2, time.Second*33000, 2, nil) + require.NoError(t, err) + require.Equal(t, time.Second*4, step) + }) + + t.Run("survive interval=0", func(t *testing.T) { + // interval=0. this should never happen, but we make sure we return something sane + // (in this case safeInterval will take care of the problem) + step, err := calculateStep(time.Second*0, time.Second*22000, 1, nil) + require.NoError(t, err) + require.Equal(t, time.Second*2, step) + }) + + t.Run("survive resolution=0", func(t *testing.T) { + // resolution=0. this should never happen, but we make sure we return something sane + // (in this case safeInterval will take care of the problem) + step, err := calculateStep(time.Second*1, time.Second*22000, 0, nil) + require.NoError(t, err) + require.Equal(t, time.Second*2, step) + }) + + t.Run("survive interval=0 and resolution=0", func(t *testing.T) { + // resolution=0 and interval=0. this should never happen, but we make sure we return something sane + // (in this case safeInterval will take care of the problem) + step, err := calculateStep(time.Second*0, time.Second*22000, 0, nil) + require.NoError(t, err) + require.Equal(t, time.Second*2, step) + }) }) } diff --git a/public/app/plugins/datasource/loki/querySplitting.test.ts b/public/app/plugins/datasource/loki/querySplitting.test.ts index 06e5c413e33..bbec5ab5829 100644 --- a/public/app/plugins/datasource/loki/querySplitting.test.ts +++ b/public/app/plugins/datasource/loki/querySplitting.test.ts @@ -40,7 +40,125 @@ describe('runSplitQuery()', () => { }); }); - test('Handles and reports rerrors', async () => { + test('Correctly splits queries without step', async () => { + await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => { + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + requestId: 'TEST_3', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + //2023-02-09T06:00:00.000Z + _i: 1675922400000, + }), + to: expect.objectContaining({ + // 2023-02-10T06:00:00.000Z + _i: 1676008800000, + }), + }), + }) + ); + + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + requestId: 'TEST_2', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + //2023-02-08T05:59:00.000Z + _i: 1675835940000, + }), + to: expect.objectContaining({ + // 2023-02-09T05:59:00.000Z + _i: 1675922340000, + }), + }), + }) + ); + + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + requestId: 'TEST_1', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + //2023-02-08T05:00:00.000Z + _i: 1675832400000, + }), + to: expect.objectContaining({ + // 2023-02-08T05:58:00.000Z + _i: 1675835880000, + }), + }), + }) + ); + }); + }); + + test('Correctly splits queries with step', async () => { + const req = { ...request }; + req.targets[0].step = '10s'; + await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => { + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + requestId: 'TEST_3', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + //2023-02-09T06:00:00.000Z + _i: 1675922400000, + }), + to: expect.objectContaining({ + // 2023-02-10T06:00:00.000Z + _i: 1676008800000, + }), + }), + }) + ); + + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + requestId: 'TEST_2', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + //2023-02-08T05:59:50.000Z + _i: 1675835990000, + }), + to: expect.objectContaining({ + // 2023-02-09T05:59:50.000Z + _i: 1675922390000, + }), + }), + }) + ); + + expect(datasource.runQuery).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + requestId: 'TEST_1', + intervalMs: 60000, + range: expect.objectContaining({ + from: expect.objectContaining({ + // 2023-02-08T05:00:00.000Z + _i: 1675832400000, + }), + to: expect.objectContaining({ + // 2023-02-08T05:59:40.000Z + _i: 1675835980000, + }), + }), + }) + ); + }); + }); + + test('Handles and reports errors', async () => { jest .spyOn(datasource, 'runQuery') .mockReturnValue(of({ state: LoadingState.Error, error: { refId: 'A', message: 'Error' }, data: [] })); @@ -339,7 +457,7 @@ describe('runSplitQuery()', () => { expect(datasource.runQuery).toHaveBeenCalledTimes(2); }); }); - test('Groups metric queries by resolution', async () => { + test('Groups metric queries with no step by calculated stepMs', async () => { const request = getQueryOptions({ targets: [ { expr: 'count_over_time({a="b"}[1m])', refId: 'A', resolution: 3 }, @@ -352,23 +470,21 @@ describe('runSplitQuery()', () => { expect(datasource.runQuery).toHaveBeenCalledTimes(2); }); }); - test('Groups mixed queries by resolution', async () => { + + test('Groups metric queries with step by stepMs', async () => { const request = getQueryOptions({ targets: [ - { expr: '{a="b"}', refId: 'A', resolution: 3 }, - { expr: '{a="b"}', refId: 'B', resolution: 5 }, - { expr: 'count_over_time({a="b"}[1m])', refId: 'C', resolution: 3 }, - { expr: 'count_over_time{a="b"}[1m])', refId: 'D', resolution: 5 }, - { expr: '{a="b"}', refId: 'E', resolution: 5, queryType: LokiQueryType.Instant }, + { expr: 'count_over_time({a="b"}[1m])', refId: 'A', resolution: 1, step: '10' }, + { expr: 'count_over_time{a="b"}[1m])', refId: 'B', resolution: 1, step: '5ms' }, ], range: range1d, }); await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => { - // A, B, C, D, E - expect(datasource.runQuery).toHaveBeenCalledTimes(5); + // A, B + expect(datasource.runQuery).toHaveBeenCalledTimes(2); }); }); - test('Chunked groups mixed queries by resolution', async () => { + test('Groups mixed queries by stepMs', async () => { const request = getQueryOptions({ targets: [ { expr: '{a="b"}', refId: 'A', resolution: 3 }, @@ -376,12 +492,32 @@ describe('runSplitQuery()', () => { { expr: 'count_over_time({a="b"}[1m])', refId: 'C', resolution: 3 }, { expr: 'count_over_time{a="b"}[1m])', refId: 'D', resolution: 5 }, { expr: '{a="b"}', refId: 'E', resolution: 5, queryType: LokiQueryType.Instant }, + { expr: 'rate({a="b"}[5m])', refId: 'F', resolution: 5, step: '10' }, + { expr: 'rate({a="b"} | logfmt[5m])', refId: 'G', resolution: 5, step: '10s' }, + ], + range: range1d, + }); + await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => { + // A, B, C, D, E, F+G + expect(datasource.runQuery).toHaveBeenCalledTimes(6); + }); + }); + test('Chunked groups mixed queries by stepMs', async () => { + const request = getQueryOptions({ + targets: [ + { expr: '{a="b"}', refId: 'A', resolution: 3 }, + { expr: '{a="b"}', refId: 'B', resolution: 5 }, + { expr: 'count_over_time({a="b"}[1m])', refId: 'C', resolution: 3 }, + { expr: 'count_over_time{a="b"}[1m])', refId: 'D', resolution: 5 }, + { expr: '{a="b"}', refId: 'E', resolution: 5, queryType: LokiQueryType.Instant }, + { expr: 'rate({a="b"}[5m])', refId: 'F', resolution: 5, step: '10' }, + { expr: 'rate({a="b"} | logfmt[5m])', refId: 'G', resolution: 5, step: '10s' }, ], range, // 3 days }); await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => { - // 3 * A, 3 * B, 3 * C, 3 * D, 1 * E - expect(datasource.runQuery).toHaveBeenCalledTimes(13); + // 3 * A, 3 * B, 3 * C, 3 * D, 1 * E, 3 * F+G + expect(datasource.runQuery).toHaveBeenCalledTimes(16); }); }); }); diff --git a/public/app/plugins/datasource/loki/querySplitting.ts b/public/app/plugins/datasource/loki/querySplitting.ts index 36fb89c61ce..f5233e107a3 100644 --- a/public/app/plugins/datasource/loki/querySplitting.ts +++ b/public/app/plugins/datasource/loki/querySplitting.ts @@ -10,6 +10,7 @@ import { dateTime, durationToMilliseconds, parseDuration, + rangeUtil, TimeRange, } from '@grafana/data'; import { LoadingState } from '@grafana/schema'; @@ -25,24 +26,15 @@ import { LokiGroupedRequest, LokiQuery, LokiQueryType } from './types'; export function partitionTimeRange( isLogsQuery: boolean, originalTimeRange: TimeRange, - intervalMs: number, - resolution: number, + stepMs: number, duration: number ): TimeRange[] { - // the `step` value that will be finally sent to Loki is rougly the same as `intervalMs`, - // but there are some complications. - // we need to replicate this algo: - // - // https://github.com/grafana/grafana/blob/main/pkg/tsdb/loki/step.go#L23 const start = originalTimeRange.from.toDate().getTime(); const end = originalTimeRange.to.toDate().getTime(); - const safeStep = Math.ceil((end - start) / 11000); - const step = Math.max(intervalMs * resolution, safeStep); - const ranges = isLogsQuery ? splitLogsTimeRange(start, end, duration) - : splitMetricTimeRange(start, end, step, duration); + : splitMetricTimeRange(start, end, stepMs, duration); return ranges.map(([start, end]) => { const from = dateTime(start); @@ -243,29 +235,20 @@ export function runSplitQuery(datasource: LokiDatasource, request: DataQueryRequ for (const resolution in resolutionPartition) { requests.push({ request: { ...request, targets: resolutionPartition[resolution] }, - partition: partitionTimeRange( - true, - request.range, - request.intervalMs, - Number(resolution), - Number(chunkRangeMs) - ), + partition: partitionTimeRange(true, request.range, request.intervalMs, Number(chunkRangeMs)), }); } } for (const [chunkRangeMs, queries] of Object.entries(rangePartitionedMetricQueries)) { - const resolutionPartition = groupBy(queries, (query) => query.resolution || 1); - for (const resolution in resolutionPartition) { + const stepMsPartition = groupBy(queries, (query) => + calculateStep(request.intervalMs, request.range, query.resolution || 1, query.step) + ); + + for (const stepMs in stepMsPartition) { requests.push({ - request: { ...request, targets: resolutionPartition[resolution] }, - partition: partitionTimeRange( - false, - request.range, - request.intervalMs, - Number(resolution), - Number(chunkRangeMs) - ), + request: { ...request, targets: stepMsPartition[stepMs] }, + partition: partitionTimeRange(false, request.range, Number(stepMs), Number(chunkRangeMs)), }); } } @@ -288,3 +271,18 @@ export function runSplitQuery(datasource: LokiDatasource, request: DataQueryRequ }) ); } + +// Replicate from backend for split queries for now, until we can move query splitting to the backend +// https://github.com/grafana/grafana/blob/main/pkg/tsdb/loki/step.go#L23 +function calculateStep(intervalMs: number, range: TimeRange, resolution: number, step: string | undefined) { + // If we can parse step,the we use it + // Otherwise we will calculate step based on interval + const interval_regex = /(-?\d+(?:\.\d+)?)(ms|[Mwdhmsy])/; + if (step?.match(interval_regex)) { + return rangeUtil.intervalToMs(step) * resolution; + } + + const newStep = intervalMs * resolution; + const safeStep = Math.round((range.to.valueOf() - range.from.valueOf()) / 11000); + return Math.max(newStep, safeStep); +} diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx index aa3d104a97d..d3f728e9269 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx @@ -38,8 +38,7 @@ describe('LokiQueryBuilderOptions', () => { }); it('can change line limit to valid value', async () => { - const { props } = setup(); - props.query.expr = '{foo="bar"}'; + const { props } = setup({ expr: '{foo="bar"}' }); await userEvent.click(screen.getByTitle('Click to edit options')); // Second autosize input is a Line limit @@ -54,10 +53,9 @@ describe('LokiQueryBuilderOptions', () => { }); it('does not change line limit to invalid numeric value', async () => { - const { props } = setup(); + const { props } = setup({ expr: '{foo="bar"}' }); // We need to start with some value to be able to change it props.query.maxLines = 10; - props.query.expr = '{foo="bar"}'; await userEvent.click(screen.getByTitle('Click to edit options')); // Second autosize input is a Line limit @@ -72,10 +70,9 @@ describe('LokiQueryBuilderOptions', () => { }); it('does not change line limit to invalid text value', async () => { - const { props } = setup(); + const { props } = setup({ expr: '{foo="bar"}' }); // We need to start with some value to be able to change it props.query.maxLines = 10; - props.query.expr = '{foo="bar"}'; await userEvent.click(screen.getByTitle('Click to edit options')); // Second autosize input is a Line limit @@ -88,6 +85,20 @@ describe('LokiQueryBuilderOptions', () => { maxLines: undefined, }); }); + + it('shows correct options for log query', async () => { + setup({ expr: '{foo="bar"}' }); + expect(screen.getByText('Line limit: 20')).toBeInTheDocument(); + expect(screen.getByText('Type: Range')).toBeInTheDocument(); + expect(screen.queryByText(/step/i)).not.toBeInTheDocument(); + }); + + it('shows correct options for metric query', async () => { + setup({ expr: 'rate({foo="bar"}[5m]', step: '1m' }); + expect(screen.queryByText('Line limit: 20')).not.toBeInTheDocument(); + expect(screen.getByText('Type: Range')).toBeInTheDocument(); + expect(screen.getByText('Step: 1m')).toBeInTheDocument(); + }); }); function setup(queryOverrides: Partial = {}) { diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx index e244294bfff..47835e0d71b 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx @@ -1,3 +1,4 @@ +import { trim } from 'lodash'; import React, { useState } from 'react'; import { CoreApp, isValidDuration, SelectableValue } from '@grafana/data'; @@ -61,14 +62,19 @@ export const LokiQueryBuilderOptions = React.memo( } } - let queryType = query.queryType ?? (query.instant ? LokiQueryType.Instant : LokiQueryType.Range); - let showMaxLines = isLogsQuery(query.expr); + function onStepChange(e: React.SyntheticEvent) { + onChange({ ...query, step: trim(e.currentTarget.value) }); + onRunQuery(); + } + + const queryType = query.queryType ?? (query.instant ? LokiQueryType.Instant : LokiQueryType.Range); + const isLogQuery = isLogsQuery(query.expr); return ( ( - {showMaxLines && ( + {isLogQuery && ( ( /> )} - - + + + )} {config.featureToggles.lokiQuerySplittingConfig && config.featureToggles.lokiQuerySplitting && ( ( } ); -function getCollapsedInfo( - query: LokiQuery, - queryType: LokiQueryType, - showMaxLines: boolean, - maxLines: number -): string[] { +function getCollapsedInfo(query: LokiQuery, queryType: LokiQueryType, maxLines: number, isLogQuery: boolean): string[] { const queryTypeLabel = queryTypeOptions.find((x) => x.value === queryType); const resolutionLabel = RESOLUTION_OPTIONS.find((x) => x.value === (query.resolution ?? 1)); @@ -152,10 +169,20 @@ function getCollapsedInfo( items.push(`Type: ${queryTypeLabel?.label}`); - if (showMaxLines) { + if (isLogQuery) { items.push(`Line limit: ${query.maxLines ?? maxLines}`); } + if (!isLogQuery) { + if (query.step) { + items.push(`Step: ${query.step}`); + } + + if (query.resolution) { + items.push(`Resolution: ${resolutionLabel?.label}`); + } + } + return items; } diff --git a/public/app/plugins/datasource/loki/tracking.test.ts b/public/app/plugins/datasource/loki/tracking.test.ts index 24d530efbdf..9f3a01151d6 100644 --- a/public/app/plugins/datasource/loki/tracking.test.ts +++ b/public/app/plugins/datasource/loki/tracking.test.ts @@ -44,7 +44,7 @@ const requests: LokiGroupedRequest[] = [ }), app: 'explore', }, - partition: partitionTimeRange(true, range, 60000, 1, 24 * 60 * 60 * 1000), + partition: partitionTimeRange(true, range, 60000, 24 * 60 * 60 * 1000), }, { request: { @@ -54,7 +54,7 @@ const requests: LokiGroupedRequest[] = [ }), app: 'explore', }, - partition: partitionTimeRange(false, range, 60000, 1, 24 * 60 * 60 * 1000), + partition: partitionTimeRange(false, range, 60000, 24 * 60 * 60 * 1000), }, ];