From f54dc7131b9ab37fa8a3c8b9b0cfd3ef56d8cfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=CC=81bor=20Farkas?= Date: Tue, 26 Jul 2022 14:47:55 +0200 Subject: [PATCH] logs: Allow disabling the logs volume histogram --- public/app/features/explore/Logs.test.tsx | 2 + public/app/features/explore/Logs.tsx | 344 ++++++++++-------- public/app/features/explore/LogsContainer.tsx | 78 ++-- public/app/features/explore/state/helpers.ts | 1 + .../app/features/explore/state/query.test.ts | 30 ++ public/app/features/explore/state/query.ts | 41 ++- public/app/features/explore/state/utils.ts | 17 + public/app/features/explore/utils/logs.ts | 8 + public/app/types/explore.ts | 1 + 9 files changed, 316 insertions(+), 206 deletions(-) create mode 100644 public/app/features/explore/utils/logs.ts diff --git a/public/app/features/explore/Logs.test.tsx b/public/app/features/explore/Logs.test.tsx index 5efc23bf0db..8a8b0d69487 100644 --- a/public/app/features/explore/Logs.test.tsx +++ b/public/app/features/explore/Logs.test.tsx @@ -18,6 +18,8 @@ describe('Logs', () => { undefined} + logsVolumeEnabled={true} + toggleLogsVolume={() => null} logsVolumeData={undefined} loadLogsVolumeData={() => undefined} logRows={rows} diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index af348a1fd15..c15ca840c13 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -33,6 +33,7 @@ import { InlineSwitch, withTheme2, Themeable2, + Collapse, } from '@grafana/ui'; import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider'; import { dedupLogRows, filterLogLevels } from 'app/core/logsModel'; @@ -42,14 +43,7 @@ import { ExploreId } from 'app/types/explore'; import { LogsMetaRow } from './LogsMetaRow'; import LogsNavigation from './LogsNavigation'; import { LogsVolumePanel } from './LogsVolumePanel'; - -const SETTINGS_KEYS = { - showLabels: 'grafana.explore.logs.showLabels', - showTime: 'grafana.explore.logs.showTime', - wrapLogMessage: 'grafana.explore.logs.wrapLogMessage', - prettifyLogMessage: 'grafana.explore.logs.prettifyLogMessage', - logsSortOrder: 'grafana.explore.logs.sortOrder', -}; +import { SETTINGS_KEYS } from './utils/logs'; interface Props extends Themeable2 { width: number; @@ -68,7 +62,9 @@ interface Props extends Themeable2 { scanRange?: RawTimeRange; exploreId: ExploreId; datasourceType?: string; + logsVolumeEnabled: boolean; logsVolumeData: DataQueryResponse | undefined; + toggleLogsVolume: (enabled: boolean) => void; loadLogsVolumeData: (exploreId: ExploreId) => void; showContextToggle?: (row?: LogRowModel) => boolean; onChangeTime: (range: AbsoluteTimeRange) => void; @@ -95,6 +91,16 @@ interface State { forceEscape: boolean; } +// We need to override css overflow of divs in Collapse element to enable sticky Logs navigation +const styleOverridesForStickyNavigation = css` + & > div { + overflow: visible; + & > div { + overflow: visible; + } + } +`; + class UnthemedLogs extends PureComponent { flipOrderTimer?: number; cancelFlippingTimer?: number; @@ -283,8 +289,10 @@ class UnthemedLogs extends PureComponent { logsMeta, logsSeries, visibleRange, + logsVolumeEnabled, logsVolumeData, loadLogsVolumeData, + toggleLogsVolume, loading = false, loadingState, onClickFilterLabel, @@ -328,163 +336,177 @@ class UnthemedLogs extends PureComponent { return ( <> - loadLogsVolumeData(exploreId)} - onHiddenSeriesChanged={this.onToggleLogLevel} - /> -
- - - - - - - - - - - - - - - ({ - label: capitalize(dedupType), - value: dedupType, - description: LogsDedupDescription[dedupType], - }))} - value={dedupStrategy} - onChange={this.onChangeDedup} - className={styles.radioButtons} - /> - - -
- - - -
-
- -
-
- { + toggleLogsVolume(isOpen); + }} + > + {logsVolumeEnabled && ( + loadLogsVolumeData(exploreId)} + onHiddenSeriesChanged={this.onToggleLogLevel} + /> + )} + + +
+ + + + + + + + + + + + + + + ({ + label: capitalize(dedupType), + value: dedupType, + description: LogsDedupDescription[dedupType], + }))} + value={dedupStrategy} + onChange={this.onChangeDedup} + className={styles.radioButtons} + /> + + +
+ + + +
+
+ +
+
+ +
+
- -
- {!loading && !hasData && !scanning && ( -
- No logs found. - -
- )} - {scanning && ( -
- {scanText} - -
- )} + {!loading && !hasData && !scanning && ( +
+ No logs found. + +
+ )} + {scanning && ( +
+ {scanText} + +
+ )} + ); } diff --git a/public/app/features/explore/LogsContainer.tsx b/public/app/features/explore/LogsContainer.tsx index 7cb00b56c4d..75bfb5784c1 100644 --- a/public/app/features/explore/LogsContainer.tsx +++ b/public/app/features/explore/LogsContainer.tsx @@ -1,4 +1,3 @@ -import { css } from '@emotion/css'; import React, { PureComponent } from 'react'; import { connect, ConnectedProps } from 'react-redux'; @@ -19,7 +18,7 @@ import { getTimeZone } from '../profile/state/selectors'; import { LiveLogsWithTheme } from './LiveLogs'; import { Logs } from './Logs'; import { splitOpen } from './state/main'; -import { addResultsToCache, clearCache, loadLogsVolumeData } from './state/query'; +import { addResultsToCache, clearCache, loadLogsVolumeData, toggleLogsVolume } from './state/query'; import { updateTimeRange } from './state/time'; import { LiveTailControls } from './useLiveTailControls'; import { LogsCrossFadeTransition } from './utils/LogsCrossFadeTransition'; @@ -104,16 +103,6 @@ class LogsContainer extends PureComponent { return null; } - // We need to override css overflow of divs in Collapse element to enable sticky Logs navigation - const styleOverridesForStickyNavigation = css` - & > div { - overflow: visible; - & > div { - overflow: visible; - } - } - `; - return ( <> @@ -133,37 +122,37 @@ class LogsContainer extends PureComponent { - - addResultsToCache(exploreId)} - clearCache={() => clearCache(exploreId)} - /> - + this.props.toggleLogsVolume(exploreId, enabled)} + logsVolumeData={logsVolumeData} + logsQueries={logsQueries} + width={width} + splitOpen={splitOpen} + loading={loading} + loadingState={loadingState} + loadLogsVolumeData={loadLogsVolumeData} + onChangeTime={this.onChangeTime} + onClickFilterLabel={onClickFilterLabel} + onClickFilterOutLabel={onClickFilterOutLabel} + onStartScanning={onStartScanning} + onStopScanning={onStopScanning} + absoluteRange={absoluteRange} + visibleRange={visibleRange} + timeZone={timeZone} + scanning={scanning} + scanRange={range.raw} + showContextToggle={this.showContextToggle} + getRowContext={this.getLogRowContext} + getFieldLinks={this.getFieldLinks} + addResultsToCache={() => addResultsToCache(exploreId)} + clearCache={() => clearCache(exploreId)} + /> ); @@ -183,6 +172,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string } isPaused, range, absoluteRange, + logsVolumeEnabled, logsVolumeDataProvider, logsVolumeData, } = item; @@ -202,6 +192,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string } isPaused, range, absoluteRange, + logsVolumeEnabled, logsVolumeDataProvider, logsVolumeData, }; @@ -213,6 +204,7 @@ const mapDispatchToProps = { addResultsToCache, clearCache, loadLogsVolumeData, + toggleLogsVolume, }; const connector = connect(mapStateToProps, mapDispatchToProps); diff --git a/public/app/features/explore/state/helpers.ts b/public/app/features/explore/state/helpers.ts index 2a9e39f46a6..b6fb357bc61 100644 --- a/public/app/features/explore/state/helpers.ts +++ b/public/app/features/explore/state/helpers.ts @@ -40,6 +40,7 @@ export const createDefaultInitialState = () => { }, cache: [], richHistory: [], + logsVolumeEnabled: true, }, }, }; diff --git a/public/app/features/explore/state/query.test.ts b/public/app/features/explore/state/query.test.ts index 7a8b85aa51f..7189e4c9db0 100644 --- a/public/app/features/explore/state/query.test.ts +++ b/public/app/features/explore/state/query.test.ts @@ -35,6 +35,7 @@ import { scanStartAction, scanStopAction, storeLogsVolumeDataProviderAction, + toggleLogsVolume, } from './query'; import { makeExplorePaneState } from './utils'; @@ -441,5 +442,34 @@ describe('reducer', () => { expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done); expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); }); + + it('do not load logsVolume data when disabled', async () => { + // turn logsvolume off + dispatch(toggleLogsVolume(ExploreId.left, false)); + expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(false); + + // verify that if we run a query, it will not do logsvolume, but the Provider will still be set + await dispatch(runQueries(ExploreId.left)); + expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); + expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeUndefined(); + expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + }); + + it('load logsVolume data when it gets enabled', async () => { + // first it is disabled + dispatch(toggleLogsVolume(ExploreId.left, false)); + + // runQueries sets up the logsVolume query, but does not run it + await dispatch(runQueries(ExploreId.left)); + expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + + // we turn logsvolume on + await dispatch(toggleLogsVolume(ExploreId.left, true)); + + // verify it was turned on + expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(true); + + expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeDefined(); + }); }); }); diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index bcb3fbfa34b..47474721ace 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -44,7 +44,7 @@ import { decorateData } from '../utils/decorators'; import { addHistoryItem, historyUpdatedAction, loadRichHistory } from './history'; import { stateSave } from './main'; import { updateTime } from './time'; -import { createCacheKey, getResultsFromCache } from './utils'; +import { createCacheKey, getResultsFromCache, storeLogsVolumeEnabled } from './utils'; // // Actions and Payloads @@ -108,6 +108,10 @@ export const queryStoreSubscriptionAction = createAction( + 'explore/toggleLogsVolumeAction' +); + export interface StoreLogsVolumeDataProvider { exploreId: ExploreId; logsVolumeDataProvider?: Observable; @@ -364,6 +368,7 @@ export const runQueries = ( refreshInterval, absoluteRange, cache, + logsVolumeEnabled, } = exploreItemState; let newQuerySub; @@ -499,6 +504,12 @@ export const runQueries = ( ); dispatch(cleanLogsVolumeAction({ exploreId })); } else if (hasLogsVolumeSupport(datasourceInstance)) { + // we always prepare the logsVolumeProvider, + // but we only load it, if the logs-volume-histogram is enabled. + // (we need to have the logsVolumeProvider always actual, + // even when the visuals are disabled, because when the user + // enables the visuals again, we need to load the histogram, + // so we need the provider) const sourceRequest = { ...transaction.request, requestId: transaction.request.requestId + '_log_volume', @@ -513,7 +524,9 @@ export const runQueries = ( const { logsVolumeData, absoluteRange } = getState().explore[exploreId]!; if (!canReuseLogsVolumeData(logsVolumeData, queries, absoluteRange)) { dispatch(cleanLogsVolumeAction({ exploreId })); - dispatch(loadLogsVolumeData(exploreId)); + if (logsVolumeEnabled) { + dispatch(loadLogsVolumeData(exploreId)); + } } } else { dispatch( @@ -619,6 +632,16 @@ export function loadLogsVolumeData(exploreId: ExploreId): ThunkResult { }; } +export function toggleLogsVolume(exploreId: ExploreId, enabled: boolean): ThunkResult { + return (dispatch, getState) => { + dispatch(toggleLogsVolumeAction({ exploreId, enabled })); + storeLogsVolumeEnabled(enabled); + if (enabled) { + dispatch(loadLogsVolumeData(exploreId)); + } + }; +} + // // Reducer // @@ -715,6 +738,20 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor }; } + if (toggleLogsVolumeAction.match(action)) { + const { enabled } = action.payload; + if (state.logsVolumeDataSubscription) { + state.logsVolumeDataSubscription.unsubscribe(); + } + return { + ...state, + logsVolumeEnabled: enabled, + // NOTE: the dataProvider is not cleared, we may need it later, + // if the user re-enables the histogram-visualization + logsVolumeData: undefined, + }; + } + if (storeLogsVolumeDataProviderAction.match(action)) { let { logsVolumeDataProvider } = action.payload; if (state.logsVolumeDataSubscription) { diff --git a/public/app/features/explore/state/utils.ts b/public/app/features/explore/state/utils.ts index 5a28d9ed7ae..03356271701 100644 --- a/public/app/features/explore/state/utils.ts +++ b/public/app/features/explore/state/utils.ts @@ -17,6 +17,7 @@ import { ExploreGraphStyle, ExploreItemState } from 'app/types/explore'; import store from '../../../core/store'; import { clearQueryKeys, lastUsedDatasourceKeyForOrgId, toGraphStyle } from '../../../core/utils/explore'; import { getDatasourceSrv } from '../../plugins/datasource_srv'; +import { SETTINGS_KEYS } from '../utils/logs'; import { toRawTimeRange } from '../utils/time'; export const DEFAULT_RANGE = { @@ -34,6 +35,21 @@ const loadGraphStyle = (): ExploreGraphStyle => { return toGraphStyle(data); }; +const LOGS_VOLUME_ENABLED_KEY = SETTINGS_KEYS.enableVolumeHistogram; +export const storeLogsVolumeEnabled = (enabled: boolean): void => { + store.set(LOGS_VOLUME_ENABLED_KEY, enabled ? 'true' : 'false'); +}; + +const loadLogsVolumeEnabled = (): boolean => { + const data = store.get(LOGS_VOLUME_ENABLED_KEY); + // we default to `enabled=true` + if (data === 'false') { + return false; + } else { + return true; + } +}; + /** * Returns a fresh Explore area state */ @@ -65,6 +81,7 @@ export const makeExplorePaneState = (): ExploreItemState => ({ eventBridge: null as unknown as EventBusExtended, cache: [], richHistory: [], + logsVolumeEnabled: loadLogsVolumeEnabled(), logsVolumeDataProvider: undefined, logsVolumeData: undefined, graphStyle: loadGraphStyle(), diff --git a/public/app/features/explore/utils/logs.ts b/public/app/features/explore/utils/logs.ts new file mode 100644 index 00000000000..0ead558c14a --- /dev/null +++ b/public/app/features/explore/utils/logs.ts @@ -0,0 +1,8 @@ +export const SETTINGS_KEYS = { + showLabels: 'grafana.explore.logs.showLabels', + showTime: 'grafana.explore.logs.showTime', + wrapLogMessage: 'grafana.explore.logs.wrapLogMessage', + prettifyLogMessage: 'grafana.explore.logs.prettifyLogMessage', + logsSortOrder: 'grafana.explore.logs.sortOrder', + enableVolumeHistogram: 'grafana.explore.logs.enableVolumeHistogram', +}; diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 3859c12cbf2..54c95517f74 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -177,6 +177,7 @@ export interface ExploreItemState { // properties below should be more generic if we add more providers // see also: DataSourceWithLogsVolumeSupport + logsVolumeEnabled: boolean; logsVolumeDataProvider?: Observable; logsVolumeDataSubscription?: SubscriptionLike; logsVolumeData?: DataQueryResponse;