Logs volume: Refactor (#60998)

* Rename log volume in redux to supp query to make it more generic

* Change enabled query to queries

* Small spell fix StoreSuppQueryDataProviderAction -> storeSuppQueryDataProviderAction

* WIP

* Improve

* WIP

* Rename

* Move to 1

* Small updates

* Use enum

* Unify naming

* Use SUPP_QUERY_TYPES instead of Object.keys()

* Move SuppQueryType to types/explore

* Rename suppQuery to supportingQuery

* Rename SUPP_QUERY_TYPES to SUPPORTING_QUERY_TYPES

* Rename supporting to supplementary 🙈

* Remove suppQueryData && suppQueryData.data[0] check as it is redundant

* Update public/app/features/explore/state/query.ts

Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>

* Update public/app/features/explore/state/query.ts

Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>

* Fix naming of SupplementaryQuery interface

Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
Ivana Huckova
2023-01-09 18:33:48 +01:00
committed by GitHub
parent 26a3c864f3
commit ddc8beda07
8 changed files with 302 additions and 159 deletions

View File

@@ -71,7 +71,7 @@ interface Props extends Themeable2 {
logsVolumeData: DataQueryResponse | undefined; logsVolumeData: DataQueryResponse | undefined;
scrollElement?: HTMLDivElement; scrollElement?: HTMLDivElement;
onSetLogsVolumeEnabled: (enabled: boolean) => void; onSetLogsVolumeEnabled: (enabled: boolean) => void;
loadLogsVolumeData: (exploreId: ExploreId) => void; loadLogsVolumeData: () => void;
showContextToggle?: (row?: LogRowModel) => boolean; showContextToggle?: (row?: LogRowModel) => boolean;
onChangeTime: (range: AbsoluteTimeRange) => void; onChangeTime: (range: AbsoluteTimeRange) => void;
onClickFilterLabel?: (key: string, value: string) => void; onClickFilterLabel?: (key: string, value: string) => void;
@@ -389,7 +389,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
onUpdateTimeRange={onChangeTime} onUpdateTimeRange={onChangeTime}
timeZone={timeZone} timeZone={timeZone}
splitOpen={splitOpen} splitOpen={splitOpen}
onLoadLogsVolume={() => loadLogsVolumeData(exploreId)} onLoadLogsVolume={loadLogsVolumeData}
onHiddenSeriesChanged={this.onToggleLogLevel} onHiddenSeriesChanged={this.onToggleLogLevel}
eventBus={this.logsVolumeEventBus} eventBus={this.logsVolumeEventBus}
/> />

View File

@@ -14,13 +14,13 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { Collapse } from '@grafana/ui'; import { Collapse } from '@grafana/ui';
import { StoreState } from 'app/types'; import { StoreState } from 'app/types';
import { ExploreId, ExploreItemState } from 'app/types/explore'; import { ExploreId, ExploreItemState, SupplementaryQueryType } from 'app/types/explore';
import { getTimeZone } from '../profile/state/selectors'; import { getTimeZone } from '../profile/state/selectors';
import { LiveLogsWithTheme } from './LiveLogs'; import { LiveLogsWithTheme } from './LiveLogs';
import { Logs } from './Logs'; import { Logs } from './Logs';
import { addResultsToCache, clearCache, loadLogsVolumeData, setLogsVolumeEnabled } from './state/query'; import { addResultsToCache, clearCache, loadSupplementaryQueryData, setSupplementaryQueryEnabled } from './state/query';
import { updateTimeRange } from './state/time'; import { updateTimeRange } from './state/time';
import { LiveTailControls } from './useLiveTailControls'; import { LiveTailControls } from './useLiveTailControls';
import { LogsCrossFadeTransition } from './utils/LogsCrossFadeTransition'; import { LogsCrossFadeTransition } from './utils/LogsCrossFadeTransition';
@@ -85,8 +85,8 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
logsMeta, logsMeta,
logsSeries, logsSeries,
logsQueries, logsQueries,
logsVolumeData, loadSupplementaryQueryData,
loadLogsVolumeData, setSupplementaryQueryEnabled,
onClickFilterLabel, onClickFilterLabel,
onClickFilterOutLabel, onClickFilterOutLabel,
onStartScanning, onStartScanning,
@@ -103,6 +103,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
addResultsToCache, addResultsToCache,
clearCache, clearCache,
scrollElement, scrollElement,
logsVolume,
} = this.props; } = this.props;
if (!logRows) { if (!logRows) {
@@ -134,15 +135,17 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
logRows={logRows} logRows={logRows}
logsMeta={logsMeta} logsMeta={logsMeta}
logsSeries={logsSeries} logsSeries={logsSeries}
logsVolumeEnabled={this.props.logsVolumeEnabled} logsVolumeEnabled={logsVolume.enabled}
onSetLogsVolumeEnabled={(enabled) => this.props.setLogsVolumeEnabled(exploreId, enabled)} onSetLogsVolumeEnabled={(enabled) =>
logsVolumeData={logsVolumeData} setSupplementaryQueryEnabled(exploreId, enabled, SupplementaryQueryType.LogsVolume)
}
logsVolumeData={logsVolume.data}
logsQueries={logsQueries} logsQueries={logsQueries}
width={width} width={width}
splitOpen={splitOpenFn} splitOpen={splitOpenFn}
loading={loading} loading={loading}
loadingState={loadingState} loadingState={loadingState}
loadLogsVolumeData={loadLogsVolumeData} loadLogsVolumeData={() => loadSupplementaryQueryData(exploreId, SupplementaryQueryType.LogsVolume)}
onChangeTime={this.onChangeTime} onChangeTime={this.onChangeTime}
onClickFilterLabel={onClickFilterLabel} onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel} onClickFilterOutLabel={onClickFilterOutLabel}
@@ -180,11 +183,10 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
isPaused, isPaused,
range, range,
absoluteRange, absoluteRange,
logsVolumeEnabled, supplementaryQueries,
logsVolumeDataProvider,
logsVolumeData,
} = item; } = item;
const timeZone = getTimeZone(state.user); const timeZone = getTimeZone(state.user);
const logsVolume = supplementaryQueries[SupplementaryQueryType.LogsVolume];
return { return {
loading, loading,
@@ -200,9 +202,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
isPaused, isPaused,
range, range,
absoluteRange, absoluteRange,
logsVolumeEnabled, logsVolume,
logsVolumeDataProvider,
logsVolumeData,
}; };
} }
@@ -210,8 +210,8 @@ const mapDispatchToProps = {
updateTimeRange, updateTimeRange,
addResultsToCache, addResultsToCache,
clearCache, clearCache,
loadLogsVolumeData, loadSupplementaryQueryData,
setLogsVolumeEnabled, setSupplementaryQueryEnabled,
}; };
const connector = connect(mapStateToProps, mapDispatchToProps); const connector = connect(mapStateToProps, mapDispatchToProps);

View File

@@ -10,7 +10,7 @@ import { ExploreId } from 'app/types/explore';
import { importQueries, runQueries } from './query'; import { importQueries, runQueries } from './query';
import { changeRefreshInterval } from './time'; import { changeRefreshInterval } from './time';
import { createEmptyQueryResponse, loadAndInitDatasource } from './utils'; import { createEmptyQueryResponse, loadAndInitDatasource, loadSupplementaryQueries } from './utils';
// //
// Actions and Payloads // Actions and Payloads
@@ -99,8 +99,7 @@ export const datasourceReducer = (state: ExploreItemState, action: AnyAction): E
graphResult: null, graphResult: null,
tableResult: null, tableResult: null,
logsResult: null, logsResult: null,
logsVolumeDataProvider: undefined, supplementaryQueries: loadSupplementaryQueries(),
logsVolumeData: undefined,
queryResponse: createEmptyQueryResponse(), queryResponse: createEmptyQueryResponse(),
loading: false, loading: false,
queryKeys: [], queryKeys: [],

View File

@@ -1,6 +1,6 @@
import { DefaultTimeZone, toUtc } from '@grafana/data'; import { DefaultTimeZone, toUtc } from '@grafana/data';
import { ExploreId } from '../../../types'; import { ExploreId, SupplementaryQueryType } from '../../../types';
export const createDefaultInitialState = () => { export const createDefaultInitialState = () => {
const t = toUtc(); const t = toUtc();
@@ -40,7 +40,11 @@ export const createDefaultInitialState = () => {
}, },
cache: [], cache: [],
richHistory: [], richHistory: [],
logsVolumeEnabled: true, supplementaryQueries: {
[SupplementaryQueryType.LogsVolume]: {
enabled: true,
},
},
}, },
}, },
}; };

View File

@@ -13,7 +13,7 @@ import {
MutableDataFrame, MutableDataFrame,
RawTimeRange, RawTimeRange,
} from '@grafana/data'; } from '@grafana/data';
import { ExploreId, ExploreItemState, StoreState, ThunkDispatch } from 'app/types'; import { ExploreId, ExploreItemState, StoreState, SupplementaryQueryType, ThunkDispatch } from 'app/types';
import { reducerTester } from '../../../../test/core/redux/reducerTester'; import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { configureStore } from '../../../store/configureStore'; import { configureStore } from '../../../store/configureStore';
@@ -26,15 +26,15 @@ import {
addResultsToCache, addResultsToCache,
cancelQueries, cancelQueries,
cancelQueriesAction, cancelQueriesAction,
cleanLogsVolumeAction, cleanSupplementaryQueryAction,
clearCache, clearCache,
importQueries, importQueries,
queryReducer, queryReducer,
runQueries, runQueries,
scanStartAction, scanStartAction,
scanStopAction, scanStopAction,
storeLogsVolumeDataProviderAction, storeSupplementaryQueryDataProviderAction,
setLogsVolumeEnabled, setSupplementaryQueryEnabled,
} from './query'; } from './query';
import { makeExplorePaneState } from './utils'; import { makeExplorePaneState } from './utils';
@@ -164,6 +164,7 @@ describe('running queries', () => {
querySubscription: unsubscribable, querySubscription: unsubscribable,
queries: ['A'], queries: ['A'],
range: testRange, range: testRange,
supplementaryQueries: { [SupplementaryQueryType.LogsVolume]: { enabled: true } },
}, },
}, },
@@ -179,8 +180,8 @@ describe('running queries', () => {
expect(dispatchedActions).toEqual([ expect(dispatchedActions).toEqual([
scanStopAction({ exploreId }), scanStopAction({ exploreId }),
cancelQueriesAction({ exploreId }), cancelQueriesAction({ exploreId }),
storeLogsVolumeDataProviderAction({ exploreId, logsVolumeDataProvider: undefined }), storeSupplementaryQueryDataProviderAction({ exploreId, type: SupplementaryQueryType.LogsVolume }),
cleanLogsVolumeAction({ exploreId }), cleanSupplementaryQueryAction({ exploreId, type: SupplementaryQueryType.LogsVolume }),
]); ]);
}); });
}); });
@@ -433,8 +434,12 @@ describe('reducer', () => {
expect(unsubscribes).toHaveLength(1); expect(unsubscribes).toHaveLength(1);
expect(unsubscribes[0]).toBeCalled(); expect(unsubscribes[0]).toBeCalled();
expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
).toBeUndefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeUndefined();
}); });
it('should load logs volume after running the query', async () => { it('should load logs volume after running the query', async () => {
@@ -448,13 +453,23 @@ describe('reducer', () => {
}; };
await dispatch(runQueries(ExploreId.left)); await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Loading); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); ).toBeDefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state
).toBe(LoadingState.Loading);
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeDefined();
await dispatch(cancelQueries(ExploreId.left)); await dispatch(cancelQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
).toBeUndefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
).toBeUndefined();
}); });
it('keeps complete log volume data when main query is canceled', async () => { it('keeps complete log volume data when main query is canceled', async () => {
@@ -466,43 +481,69 @@ describe('reducer', () => {
}; };
await dispatch(runQueries(ExploreId.left)); await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); ).toBeDefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state
).toBe(LoadingState.Done);
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeDefined();
await dispatch(cancelQueries(ExploreId.left)); await dispatch(cancelQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); ).toBeDefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state
).toBe(LoadingState.Done);
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeUndefined();
}); });
it('do not load logsVolume data when disabled', async () => { it('do not load logsVolume data when disabled', async () => {
// turn logsvolume off // turn logsvolume off
dispatch(setLogsVolumeEnabled(ExploreId.left, false)); dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume));
expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(false); expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(
false
);
// verify that if we run a query, it will not do logsvolume, but the Provider will still be set // verify that if we run a query, it will not do logsvolume, but the Provider will still be set
await dispatch(runQueries(ExploreId.left)); await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); expect(
expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeUndefined(); getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); ).toBeUndefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
).toBeUndefined();
expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeDefined();
}); });
it('load logsVolume data when it gets enabled', async () => { it('load logsVolume data when it gets enabled', async () => {
// first it is disabled // first it is disabled
dispatch(setLogsVolumeEnabled(ExploreId.left, false)); dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume));
// runQueries sets up the logsVolume query, but does not run it // runQueries sets up the logsVolume query, but does not run it
await dispatch(runQueries(ExploreId.left)); await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
).toBeDefined();
// we turn logsvolume on // we turn logsvolume on
await dispatch(setLogsVolumeEnabled(ExploreId.left, true)); await dispatch(setSupplementaryQueryEnabled(ExploreId.left, true, SupplementaryQueryType.LogsVolume));
// verify it was turned on // verify it was turned on
expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(true); expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(
true
);
expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeDefined(); expect(
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
).toBeDefined();
}); });
}); });
}); });

View File

@@ -36,7 +36,7 @@ import { getTimeZone } from 'app/features/profile/state/selectors';
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
import { store } from 'app/store/store'; import { store } from 'app/store/store';
import { ExploreItemState, ExplorePanelData, ThunkDispatch, ThunkResult } from 'app/types'; import { ExploreItemState, ExplorePanelData, ThunkDispatch, ThunkResult } from 'app/types';
import { ExploreId, ExploreState, QueryOptions } from 'app/types/explore'; import { ExploreId, ExploreState, QueryOptions, SupplementaryQueryType, SupplementaryQueries } from 'app/types/explore';
import { notifyApp } from '../../../core/actions'; import { notifyApp } from '../../../core/actions';
import { createErrorNotification } from '../../../core/copy/appNotification'; import { createErrorNotification } from '../../../core/copy/appNotification';
@@ -46,7 +46,12 @@ import { decorateData } from '../utils/decorators';
import { addHistoryItem, historyUpdatedAction, loadRichHistory } from './history'; import { addHistoryItem, historyUpdatedAction, loadRichHistory } from './history';
import { stateSave } from './main'; import { stateSave } from './main';
import { updateTime } from './time'; import { updateTime } from './time';
import { createCacheKey, getResultsFromCache, storeLogsVolumeEnabled } from './utils'; import {
createCacheKey,
getResultsFromCache,
storeSupplementaryQueryEnabled,
SUPPLEMENTARY_QUERY_TYPES,
} from './utils';
// //
// Actions and Payloads // Actions and Payloads
@@ -95,43 +100,50 @@ export const queryStoreSubscriptionAction = createAction<QueryStoreSubscriptionP
'explore/queryStoreSubscription' 'explore/queryStoreSubscription'
); );
const setLogsVolumeEnabledAction = createAction<{ exploreId: ExploreId; enabled: boolean }>( const setSupplementaryQueryEnabledAction = createAction<{
'explore/setLogsVolumeEnabledAction'
);
export interface StoreLogsVolumeDataProvider {
exploreId: ExploreId; exploreId: ExploreId;
logsVolumeDataProvider?: Observable<DataQueryResponse>; type: SupplementaryQueryType;
enabled: boolean;
}>('explore/setSupplementaryQueryEnabledAction');
export interface StoreSupplementaryQueryDataProvider {
exploreId: ExploreId;
dataProvider?: Observable<DataQueryResponse>;
type: SupplementaryQueryType;
} }
/** /**
* Stores available logs volume provider after running the query. Used internally by runQueries(). * Stores available logs volume provider after running the query. Used internally by runQueries().
*/ */
export const storeLogsVolumeDataProviderAction = createAction<StoreLogsVolumeDataProvider>( export const storeSupplementaryQueryDataProviderAction = createAction<StoreSupplementaryQueryDataProvider>(
'explore/storeLogsVolumeDataProviderAction' 'explore/storeSupplementaryQueryDataProviderAction'
); );
export const cleanLogsVolumeAction = createAction<{ exploreId: ExploreId }>('explore/cleanLogsVolumeAction'); export const cleanSupplementaryQueryAction = createAction<{ exploreId: ExploreId; type: SupplementaryQueryType }>(
'explore/cleanSupplementaryQueryAction'
);
export interface StoreLogsVolumeDataSubscriptionPayload { export interface StoreSupplementaryQueryDataSubscriptionPayload {
exploreId: ExploreId; exploreId: ExploreId;
logsVolumeDataSubscription?: SubscriptionLike; dataSubscription?: SubscriptionLike;
type: SupplementaryQueryType;
} }
/** /**
* Stores current logs volume subscription for given explore pane. * Stores current logs volume subscription for given explore pane.
*/ */
const storeLogsVolumeDataSubscriptionAction = createAction<StoreLogsVolumeDataSubscriptionPayload>( const storeSupplementaryQueryDataSubscriptionAction = createAction<StoreSupplementaryQueryDataSubscriptionPayload>(
'explore/storeLogsVolumeDataSubscriptionAction' 'explore/storeSupplementaryQueryDataSubscriptionAction'
); );
/** /**
* Stores data returned by the provider. Used internally by loadLogsVolumeData(). * Stores data returned by the provider. Used internally by loadSupplementaryQueryData().
*/ */
const updateLogsVolumeDataAction = createAction<{ const updateSupplementaryQueryDataAction = createAction<{
exploreId: ExploreId; exploreId: ExploreId;
logsVolumeData: DataQueryResponse; type: SupplementaryQueryType;
}>('explore/updateLogsVolumeDataAction'); data: DataQueryResponse;
}>('explore/updateSupplementaryQueryDataAction');
export interface QueryEndedPayload { export interface QueryEndedPayload {
exploreId: ExploreId; exploreId: ExploreId;
@@ -231,15 +243,16 @@ export function cancelQueries(exploreId: ExploreId): ThunkResult<void> {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(scanStopAction({ exploreId })); dispatch(scanStopAction({ exploreId }));
dispatch(cancelQueriesAction({ exploreId })); dispatch(cancelQueriesAction({ exploreId }));
dispatch(
storeLogsVolumeDataProviderAction({ const supplementaryQueries = getState().explore[exploreId]!.supplementaryQueries;
exploreId, // Cancel all data providers
logsVolumeDataProvider: undefined, for (const type of SUPPLEMENTARY_QUERY_TYPES) {
}) dispatch(storeSupplementaryQueryDataProviderAction({ exploreId, dataProvider: undefined, type }));
);
// clear any incomplete data // And clear any incomplete data
if (getState().explore[exploreId]!.logsVolumeData?.state !== LoadingState.Done) { if (supplementaryQueries[type]?.data?.state !== LoadingState.Done) {
dispatch(cleanLogsVolumeAction({ exploreId })); dispatch(cleanSupplementaryQueryAction({ exploreId, type }));
}
} }
dispatch(stateSave()); dispatch(stateSave());
}; };
@@ -425,7 +438,6 @@ export const runQueries = (
refreshInterval, refreshInterval,
absoluteRange, absoluteRange,
cache, cache,
logsVolumeEnabled,
} = exploreItemState; } = exploreItemState;
let newQuerySub; let newQuerySub;
@@ -558,13 +570,25 @@ export const runQueries = (
}); });
if (live) { if (live) {
dispatch( for (const type of SUPPLEMENTARY_QUERY_TYPES) {
storeLogsVolumeDataProviderAction({ dispatch(
exploreId, storeSupplementaryQueryDataProviderAction({
logsVolumeDataProvider: undefined, exploreId,
}) dataProvider: undefined,
); type,
dispatch(cleanLogsVolumeAction({ exploreId })); })
);
dispatch(cleanSupplementaryQueryAction({ exploreId, type }));
}
// In this whole part., we need to figure out
// checking the type of enabled supp queries
// then for which enabled supp queries has data source support
// and then we need to run the supp queries
// but we need to make sure that supp queries that dont work
// return undefined provider
// we should also make sure we store the type of provider that
// was last stored
} else if (hasLogsVolumeSupport(datasourceInstance)) { } else if (hasLogsVolumeSupport(datasourceInstance)) {
// we always prepare the logsVolumeProvider, // we always prepare the logsVolumeProvider,
// but we only load it, if the logs-volume-histogram is enabled. // but we only load it, if the logs-volume-histogram is enabled.
@@ -576,25 +600,29 @@ export const runQueries = (
...transaction.request, ...transaction.request,
requestId: transaction.request.requestId + '_log_volume', requestId: transaction.request.requestId + '_log_volume',
}; };
const logsVolumeDataProvider = datasourceInstance.getLogsVolumeDataProvider(sourceRequest); const type = SupplementaryQueryType.LogsVolume;
const dataProvider = datasourceInstance.getLogsVolumeDataProvider(sourceRequest);
dispatch( dispatch(
storeLogsVolumeDataProviderAction({ storeSupplementaryQueryDataProviderAction({
exploreId, exploreId,
logsVolumeDataProvider, type,
dataProvider,
}) })
); );
const { logsVolumeData, absoluteRange } = getState().explore[exploreId]!;
if (!canReuseLogsVolumeData(logsVolumeData, queries, absoluteRange)) { const { supplementaryQueries, absoluteRange } = getState().explore[exploreId]!;
dispatch(cleanLogsVolumeAction({ exploreId })); if (!canReuseSupplementaryQueryData(supplementaryQueries[type].data, queries, absoluteRange)) {
if (logsVolumeEnabled) { dispatch(cleanSupplementaryQueryAction({ exploreId, type }));
dispatch(loadLogsVolumeData(exploreId)); if (supplementaryQueries[type].enabled) {
dispatch(loadSupplementaryQueryData(exploreId, type));
} }
} }
} else { } else {
dispatch( dispatch(
storeLogsVolumeDataProviderAction({ storeSupplementaryQueryDataProviderAction({
exploreId, exploreId,
logsVolumeDataProvider: undefined, dataProvider: undefined,
type: SupplementaryQueryType.LogsVolume,
}) })
); );
} }
@@ -605,20 +633,20 @@ export const runQueries = (
}; };
/** /**
* Checks if after changing the time range the existing data can be used to show logs volume. * Checks if after changing the time range the existing data can be used to show supplementary query.
* It can happen if queries are the same and new time range is within existing data time range. * It can happen if queries are the same and new time range is within existing data time range.
*/ */
function canReuseLogsVolumeData( function canReuseSupplementaryQueryData(
logsVolumeData: DataQueryResponse | undefined, supplementaryQueryData: DataQueryResponse | undefined,
queries: DataQuery[], queries: DataQuery[],
selectedTimeRange: AbsoluteTimeRange selectedTimeRange: AbsoluteTimeRange
): boolean { ): boolean {
if (logsVolumeData && logsVolumeData.data[0]) { if (supplementaryQueryData && supplementaryQueryData.data[0]) {
// check if queries are the same // check if queries are the same
if (!deepEqual(logsVolumeData.data[0].meta?.custom?.targets, queries)) { if (!deepEqual(supplementaryQueryData.data[0].meta?.custom?.targets, queries)) {
return false; return false;
} }
const dataRange = logsVolumeData && logsVolumeData.data[0] && logsVolumeData.data[0].meta?.custom?.absoluteRange; const dataRange = supplementaryQueryData.data[0].meta?.custom?.absoluteRange;
// if selected range is within loaded logs volume // if selected range is within loaded logs volume
if (dataRange && dataRange.from <= selectedTimeRange.from && selectedTimeRange.to <= dataRange.to) { if (dataRange && dataRange.from <= selectedTimeRange.from && selectedTimeRange.to <= dataRange.to) {
return true; return true;
@@ -664,7 +692,7 @@ export function addResultsToCache(exploreId: ExploreId): ThunkResult<void> {
const absoluteRange = getState().explore[exploreId]!.absoluteRange; const absoluteRange = getState().explore[exploreId]!.absoluteRange;
const cacheKey = createCacheKey(absoluteRange); const cacheKey = createCacheKey(absoluteRange);
// Save results to cache only when all results recived and loading is done // Save results to cache only when all results received and loading is done
if (queryResponse.state === LoadingState.Done) { if (queryResponse.state === LoadingState.Done) {
dispatch(addResultsToCacheAction({ exploreId, cacheKey, queryResponse })); dispatch(addResultsToCacheAction({ exploreId, cacheKey, queryResponse }));
} }
@@ -680,26 +708,38 @@ export function clearCache(exploreId: ExploreId): ThunkResult<void> {
/** /**
* Initializes loading logs volume data and stores emitted value. * Initializes loading logs volume data and stores emitted value.
*/ */
export function loadLogsVolumeData(exploreId: ExploreId): ThunkResult<void> { export function loadSupplementaryQueryData(exploreId: ExploreId, type: SupplementaryQueryType): ThunkResult<void> {
return (dispatch, getState) => { return (dispatch, getState) => {
const { logsVolumeDataProvider } = getState().explore[exploreId]!; const { supplementaryQueries } = getState().explore[exploreId]!;
if (logsVolumeDataProvider) { const dataProvider = supplementaryQueries[type].dataProvider;
const logsVolumeDataSubscription = logsVolumeDataProvider.subscribe({
next: (logsVolumeData: DataQueryResponse) => { if (dataProvider) {
dispatch(updateLogsVolumeDataAction({ exploreId, logsVolumeData })); const dataSubscription = dataProvider.subscribe({
next: (supplementaryQueryData: DataQueryResponse) => {
dispatch(updateSupplementaryQueryDataAction({ exploreId, type, data: supplementaryQueryData }));
}, },
}); });
dispatch(storeLogsVolumeDataSubscriptionAction({ exploreId, logsVolumeDataSubscription })); dispatch(
storeSupplementaryQueryDataSubscriptionAction({
exploreId,
type,
dataSubscription,
})
);
} }
}; };
} }
export function setLogsVolumeEnabled(exploreId: ExploreId, enabled: boolean): ThunkResult<void> { export function setSupplementaryQueryEnabled(
exploreId: ExploreId,
enabled: boolean,
type: SupplementaryQueryType
): ThunkResult<void> {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(setLogsVolumeEnabledAction({ exploreId, enabled })); dispatch(setSupplementaryQueryEnabledAction({ exploreId, enabled, type }));
storeLogsVolumeEnabled(enabled); storeSupplementaryQueryEnabled(enabled, type);
if (enabled) { if (enabled) {
dispatch(loadLogsVolumeData(exploreId)); dispatch(loadSupplementaryQueryData(exploreId, type));
} }
}; };
} }
@@ -763,53 +803,87 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor
}; };
} }
if (setLogsVolumeEnabledAction.match(action)) { if (setSupplementaryQueryEnabledAction.match(action)) {
const { enabled } = action.payload; const { enabled, type } = action.payload;
if (!enabled && state.logsVolumeDataSubscription) { const { supplementaryQueries } = state;
state.logsVolumeDataSubscription.unsubscribe(); const dataSubscription = supplementaryQueries[type].dataSubscription;
if (!enabled && dataSubscription) {
dataSubscription.unsubscribe();
} }
return {
...state, const nextSupplementaryQueries: SupplementaryQueries = {
logsVolumeEnabled: enabled, ...supplementaryQueries,
// NOTE: the dataProvider is not cleared, we may need it later, // NOTE: the dataProvider is not cleared, we may need it later,
// if the user re-enables the histogram-visualization // if the user re-enables the supplementary query
logsVolumeData: undefined, [type]: { ...supplementaryQueries[type], enabled, data: undefined },
};
return {
...state,
supplementaryQueries: nextSupplementaryQueries,
}; };
} }
if (storeLogsVolumeDataProviderAction.match(action)) { if (storeSupplementaryQueryDataProviderAction.match(action)) {
let { logsVolumeDataProvider } = action.payload; const { dataProvider, type } = action.payload;
if (state.logsVolumeDataSubscription) { const { supplementaryQueries } = state;
state.logsVolumeDataSubscription.unsubscribe(); const supplementaryQuery = supplementaryQueries[type];
if (supplementaryQuery?.dataSubscription) {
supplementaryQuery.dataSubscription.unsubscribe();
} }
const nextSupplementaryQueries = {
...supplementaryQueries,
[type]: { ...supplementaryQuery, dataProvider, dataSubscription: undefined },
};
return { return {
...state, ...state,
logsVolumeDataProvider, supplementaryQueries: nextSupplementaryQueries,
logsVolumeDataSubscription: undefined,
}; };
} }
if (cleanLogsVolumeAction.match(action)) { if (cleanSupplementaryQueryAction.match(action)) {
const { type } = action.payload;
const { supplementaryQueries } = state;
const nextSupplementaryQueries = {
...supplementaryQueries,
[type]: { ...supplementaryQueries[type], data: undefined },
};
return { return {
...state, ...state,
logsVolumeData: undefined, supplementaryQueries: nextSupplementaryQueries,
}; };
} }
if (storeLogsVolumeDataSubscriptionAction.match(action)) { if (storeSupplementaryQueryDataSubscriptionAction.match(action)) {
const { logsVolumeDataSubscription } = action.payload; const { dataSubscription, type } = action.payload;
const { supplementaryQueries } = state;
const nextSupplementaryQueries = {
...supplementaryQueries,
[type]: { ...supplementaryQueries[type], dataSubscription },
};
return { return {
...state, ...state,
logsVolumeDataSubscription, supplementaryQueries: nextSupplementaryQueries,
}; };
} }
if (updateLogsVolumeDataAction.match(action)) { if (updateSupplementaryQueryDataAction.match(action)) {
let { logsVolumeData } = action.payload; let { data, type } = action.payload;
const { supplementaryQueries } = state;
const nextSupplementaryQueries = {
...supplementaryQueries,
[type]: { ...supplementaryQueries[type], data },
};
return { return {
...state, ...state,
logsVolumeData, supplementaryQueries: nextSupplementaryQueries,
}; };
} }

View File

@@ -12,7 +12,7 @@ import {
PanelData, PanelData,
} from '@grafana/data'; } from '@grafana/data';
import { ExplorePanelData } from 'app/types'; import { ExplorePanelData } from 'app/types';
import { ExploreItemState } from 'app/types/explore'; import { ExploreItemState, SupplementaryQueries, SupplementaryQueryType } from 'app/types/explore';
import store from '../../../core/store'; import store from '../../../core/store';
import { clearQueryKeys, lastUsedDatasourceKeyForOrgId } from '../../../core/utils/explore'; import { clearQueryKeys, lastUsedDatasourceKeyForOrgId } from '../../../core/utils/explore';
@@ -20,6 +20,14 @@ import { getDatasourceSrv } from '../../plugins/datasource_srv';
import { SETTINGS_KEYS } from '../utils/logs'; import { SETTINGS_KEYS } from '../utils/logs';
import { toRawTimeRange } from '../utils/time'; import { toRawTimeRange } from '../utils/time';
export const SUPPLEMENTARY_QUERY_TYPES: SupplementaryQueryType[] = [SupplementaryQueryType.LogsVolume];
// Used to match supplementaryQueryType to corresponding local storage key
// TODO: Remove this and unify enum values with SETTINGS_KEYS.enableVolumeHistogram
const supplementaryQuerySettings: { [key in SupplementaryQueryType]: string } = {
[SupplementaryQueryType.LogsVolume]: SETTINGS_KEYS.enableVolumeHistogram,
};
export const DEFAULT_RANGE = { export const DEFAULT_RANGE = {
from: 'now-6h', from: 'now-6h',
to: 'now', to: 'now',
@@ -30,19 +38,25 @@ export const storeGraphStyle = (graphStyle: string): void => {
store.set(GRAPH_STYLE_KEY, graphStyle); store.set(GRAPH_STYLE_KEY, graphStyle);
}; };
const LOGS_VOLUME_ENABLED_KEY = SETTINGS_KEYS.enableVolumeHistogram; export const storeSupplementaryQueryEnabled = (enabled: boolean, type: SupplementaryQueryType): void => {
export const storeLogsVolumeEnabled = (enabled: boolean): void => { if (supplementaryQuerySettings[type]) {
store.set(LOGS_VOLUME_ENABLED_KEY, enabled ? 'true' : 'false'); store.set(supplementaryQuerySettings[type], enabled ? 'true' : 'false');
}
}; };
const loadLogsVolumeEnabled = (): boolean => { export const loadSupplementaryQueries = (): SupplementaryQueries => {
const data = store.get(LOGS_VOLUME_ENABLED_KEY); // We default to true for all supp queries
// we default to `enabled=true` let supplementaryQueries: SupplementaryQueries = {
if (data === 'false') { [SupplementaryQueryType.LogsVolume]: { enabled: true },
return false; };
}
return true; for (const type of SUPPLEMENTARY_QUERY_TYPES) {
// Only if "false" value in local storage, we disable it
if (store.get(supplementaryQuerySettings[type]) === 'false') {
supplementaryQueries[type] = { enabled: false };
}
}
return supplementaryQueries;
}; };
/** /**
@@ -77,9 +91,7 @@ export const makeExplorePaneState = (): ExploreItemState => ({
eventBridge: null as unknown as EventBusExtended, eventBridge: null as unknown as EventBusExtended,
cache: [], cache: [],
richHistory: [], richHistory: [],
logsVolumeEnabled: loadLogsVolumeEnabled(), supplementaryQueries: loadSupplementaryQueries(),
logsVolumeDataProvider: undefined,
logsVolumeData: undefined,
panelsState: {}, panelsState: {},
}); });

View File

@@ -204,12 +204,10 @@ export interface ExploreItemState {
*/ */
cache: Array<{ key: string; value: ExplorePanelData }>; cache: Array<{ key: string; value: ExplorePanelData }>;
// properties below should be more generic if we add more providers /**
// see also: DataSourceWithLogsVolumeSupport * Supplementary queries are additional queries used in Explore, e.g. for logs volume
logsVolumeEnabled: boolean; */
logsVolumeDataProvider?: Observable<DataQueryResponse>; supplementaryQueries: SupplementaryQueries;
logsVolumeDataSubscription?: SubscriptionLike;
logsVolumeData?: DataQueryResponse;
panelsState: ExplorePanelsState; panelsState: ExplorePanelsState;
@@ -270,3 +268,18 @@ export enum TABLE_RESULTS_STYLE {
} }
export const TABLE_RESULTS_STYLES = [TABLE_RESULTS_STYLE.table, TABLE_RESULTS_STYLE.raw]; export const TABLE_RESULTS_STYLES = [TABLE_RESULTS_STYLE.table, TABLE_RESULTS_STYLE.raw];
export type TableResultsStyle = typeof TABLE_RESULTS_STYLES[number]; export type TableResultsStyle = typeof TABLE_RESULTS_STYLES[number];
export interface SupplementaryQuery {
enabled: boolean;
dataProvider?: Observable<DataQueryResponse>;
dataSubscription?: SubscriptionLike;
data?: DataQueryResponse;
}
export type SupplementaryQueries = {
[key in SupplementaryQueryType]: SupplementaryQuery;
};
export enum SupplementaryQueryType {
LogsVolume = 'LogsVolume',
}