From 82cf1d334ca352fda6b252fac5b256815a7668e0 Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Tue, 7 Jan 2025 14:20:37 +0000 Subject: [PATCH] Logs: Issue queries in forward or backward direction depending on the selected sorting option (#970) * feat(LogsPanelScene): issue queries in forward or backward direction depending on sort * fix(LogsPanelScene): improve query rerunning * test(sortOrder): update content expectation --- .../ServiceScene/LogsPanelScene.tsx | 9 ++++++ src/services/datasource.ts | 30 ++++++++++++++----- src/services/lokiQuery.ts | 7 +++++ tests/exploreServicesBreakDown.spec.ts | 4 --- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/Components/ServiceScene/LogsPanelScene.tsx b/src/Components/ServiceScene/LogsPanelScene.tsx index a94cf1bd2..fa4b278bb 100644 --- a/src/Components/ServiceScene/LogsPanelScene.tsx +++ b/src/Components/ServiceScene/LogsPanelScene.tsx @@ -5,6 +5,7 @@ import { sceneGraph, SceneObjectBase, SceneObjectState, + SceneQueryRunner, VizPanel, } from '@grafana/scenes'; import { DataFrame, getValueFormat, LogRowModel } from '@grafana/data'; @@ -104,6 +105,14 @@ export class LogsPanelScene extends SceneObjectBase { if (!this.state.body) { return; } + if ('sortOrder' in options) { + const $data = sceneGraph.getData(this); + const queryRunner = + $data instanceof SceneQueryRunner ? $data : sceneGraph.findDescendents($data, SceneQueryRunner)[0]; + if (queryRunner) { + queryRunner.runQueries(); + } + } this.state.body.onOptionsChange(options); } diff --git a/src/services/datasource.ts b/src/services/datasource.ts index ee46c0bbb..028a99ec2 100644 --- a/src/services/datasource.ts +++ b/src/services/datasource.ts @@ -10,7 +10,7 @@ import { } from '@grafana/data'; import { config, DataSourceWithBackend, getDataSourceSrv } from '@grafana/runtime'; import { RuntimeDataSource, sceneUtils } from '@grafana/scenes'; -import { DataQuery } from '@grafana/schema'; +import { DataQuery, LogsSortOrder } from '@grafana/schema'; import { Observable, Subscriber } from 'rxjs'; import { getDataSource } from './scenes'; import { getPrimaryLabelFromUrl } from './routing'; @@ -19,11 +19,13 @@ import { FIELDS_TO_REMOVE, LABELS_TO_REMOVE, sortLabelsByCardinality } from './f import { SERVICE_NAME } from './variables'; import { runShardSplitQuery } from './shardQuerySplitting'; import { requestSupportsSharding } from './logql'; -import { LokiDatasource, LokiQuery } from './lokiQuery'; +import { LokiDatasource, LokiQuery, LokiQueryDirection } from './lokiQuery'; import { SceneDataQueryRequest, SceneDataQueryResourceRequest, VolumeRequestProps } from './datasourceTypes'; import { logger } from './logger'; import { PLUGIN_ID } from './plugin'; import { sanitizeStreamSelector } from './query'; +import { LOGS_PANEL_QUERY_REFID } from 'Components/ServiceScene/ServiceScene'; +import { getLogsPanelSortOrder } from 'Components/ServiceScene/LogOptionsScene'; export const WRAPPED_LOKI_DS_UID = 'wrapped-loki-ds-uid'; @@ -141,11 +143,13 @@ export class WrappedLokiDatasource extends RuntimeDataSource { const updatedRequest = { ...request, - targets: ds.interpolateVariablesInQueries(request.targets, request.scopedVars).map((target) => ({ - ...target, - resource: undefined, - expr: sanitizeStreamSelector(target.expr), - })), + targets: this.applyQueryDirection( + ds.interpolateVariablesInQueries(request.targets, request.scopedVars).map((target) => ({ + ...target, + resource: undefined, + expr: sanitizeStreamSelector(target.expr), + })) + ), }; // Query the datasource and return either observable or promise @@ -266,6 +270,18 @@ export class WrappedLokiDatasource extends RuntimeDataSource { return { interpolatedTarget, expression }; } + private applyQueryDirection(targets: LokiQuery[]) { + const sortOrder = getLogsPanelSortOrder(); + return targets.map((target) => { + if (target.refId !== LOGS_PANEL_QUERY_REFID) { + return target; + } + target.direction = + sortOrder === LogsSortOrder.Descending ? LokiQueryDirection.Backward : LokiQueryDirection.Forward; + return target; + }); + } + private async getDetectedLabels( request: DataQueryRequest, ds: LokiDatasource, diff --git a/src/services/lokiQuery.ts b/src/services/lokiQuery.ts index d04c24028..936af53cd 100644 --- a/src/services/lokiQuery.ts +++ b/src/services/lokiQuery.ts @@ -3,6 +3,12 @@ import { DataSourceRef } from '@grafana/schema'; import { DataSourceWithBackend } from '@grafana/runtime'; import { DataSourceJsonData } from '@grafana/data'; +export enum LokiQueryDirection { + Backward = 'backward', + Forward = 'forward', + Scan = 'scan', +} + export type LokiQuery = { refId: string; queryType?: LokiQueryType; @@ -13,6 +19,7 @@ export type LokiQuery = { splitDuration?: string; datasource?: DataSourceRef; maxLines?: number; + direction?: LokiQueryDirection; }; export type LokiQueryType = 'instant' | 'range' | 'stream' | string; diff --git a/tests/exploreServicesBreakDown.spec.ts b/tests/exploreServicesBreakDown.spec.ts index d09b9b2b7..a74d8a641 100644 --- a/tests/exploreServicesBreakDown.spec.ts +++ b/tests/exploreServicesBreakDown.spec.ts @@ -1092,10 +1092,6 @@ test.describe('explore services breakdown page', () => { // Scroll the whole page to the bottom so the whole logs panel is visible await explorePage.scrollToBottom(); - // The logs panel keeps the lines in the viewport the same, but will scroll us down - await expect(firstRow).not.toBeInViewport(); - await expect(page.getByText(newestLogContent)).toBeInViewport(); - // assert timestamps are ASC (oldest first) expect(new Date(await firstRowTimeCell.textContent()).valueOf()).toBeLessThanOrEqual( new Date(await secondRowTimeCell.textContent()).valueOf()