mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
Logs: Allow collapsing the logs volume histogram (#52808)
* logs: Allow disabling the logs volume histogram
* removed unnecessary code
* refactor: adjusted prop-name
* refactor: simplified code
* refactor: more readable code
* refactor: better naming
* refactor: better naming
* only unsubscribe when disabling the UI
* revert visual change
* re-apply visual change
This reverts commit 7fda5c197f
.
* smoother transition
* adjusted visual
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
* logs: top-gap reduced
* added feature tracking
* removed not-working feature-tracking property
* lint fix
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
This commit is contained in:
parent
2efd7fa481
commit
d8b4f776aa
@ -18,6 +18,8 @@ describe('Logs', () => {
|
||||
<Logs
|
||||
exploreId={ExploreId.left}
|
||||
splitOpen={() => undefined}
|
||||
logsVolumeEnabled={true}
|
||||
onSetLogsVolumeEnabled={() => null}
|
||||
logsVolumeData={undefined}
|
||||
loadLogsVolumeData={() => undefined}
|
||||
logRows={rows}
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
InlineSwitch,
|
||||
withTheme2,
|
||||
Themeable2,
|
||||
Collapse,
|
||||
} from '@grafana/ui';
|
||||
import { dedupLogRows, filterLogLevels } from 'app/core/logsModel';
|
||||
import store from 'app/core/store';
|
||||
@ -43,14 +44,7 @@ import { LogRows } from '../logs/components/LogRows';
|
||||
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;
|
||||
@ -69,7 +63,9 @@ interface Props extends Themeable2 {
|
||||
scanRange?: RawTimeRange;
|
||||
exploreId: ExploreId;
|
||||
datasourceType?: string;
|
||||
logsVolumeEnabled: boolean;
|
||||
logsVolumeData: DataQueryResponse | undefined;
|
||||
onSetLogsVolumeEnabled: (enabled: boolean) => void;
|
||||
loadLogsVolumeData: (exploreId: ExploreId) => void;
|
||||
showContextToggle?: (row?: LogRowModel) => boolean;
|
||||
onChangeTime: (range: AbsoluteTimeRange) => void;
|
||||
@ -96,6 +92,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<Props, State> {
|
||||
flipOrderTimer?: number;
|
||||
cancelFlippingTimer?: number;
|
||||
@ -201,6 +207,14 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
this.setState({ hiddenLogLevels });
|
||||
};
|
||||
|
||||
onToggleLogsVolumeCollapse = (isOpen: boolean) => {
|
||||
this.props.onSetLogsVolumeEnabled(isOpen);
|
||||
reportInteraction('grafana_explore_logs_histogram_toggle_clicked', {
|
||||
datasourceType: this.props.datasourceType,
|
||||
type: isOpen ? 'open' : 'close',
|
||||
});
|
||||
};
|
||||
|
||||
onClickScan = (event: React.SyntheticEvent) => {
|
||||
event.preventDefault();
|
||||
if (this.props.onStartScanning) {
|
||||
@ -284,6 +298,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
logsMeta,
|
||||
logsSeries,
|
||||
visibleRange,
|
||||
logsVolumeEnabled,
|
||||
logsVolumeData,
|
||||
loadLogsVolumeData,
|
||||
loading = false,
|
||||
@ -329,163 +344,169 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<LogsVolumePanel
|
||||
absoluteRange={absoluteRange}
|
||||
width={width}
|
||||
logsVolumeData={logsVolumeData}
|
||||
logLinesBasedData={
|
||||
logsSeries
|
||||
? {
|
||||
data: logsSeries,
|
||||
state: loadingState,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
logLinesBasedDataVisibleRange={visibleRange}
|
||||
onUpdateTimeRange={onChangeTime}
|
||||
timeZone={timeZone}
|
||||
splitOpen={splitOpen}
|
||||
onLoadLogsVolume={() => loadLogsVolumeData(exploreId)}
|
||||
onHiddenSeriesChanged={this.onToggleLogLevel}
|
||||
/>
|
||||
<div className={styles.logOptions} ref={this.topLogsRef}>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Time" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={showTime}
|
||||
onChange={this.onChangeTime}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`show-time_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Unique labels" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={showLabels}
|
||||
onChange={this.onChangeLabels}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`unique-labels_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Wrap lines" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={wrapLogMessage}
|
||||
onChange={this.onChangeWrapLogMessage}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`wrap-lines_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Prettify JSON" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={prettifyLogMessage}
|
||||
onChange={this.onChangePrettifyLogMessage}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`prettify_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Dedup" className={styles.horizontalInlineLabel} transparent>
|
||||
<RadioButtonGroup
|
||||
options={Object.values(LogsDedupStrategy).map((dedupType) => ({
|
||||
label: capitalize(dedupType),
|
||||
value: dedupType,
|
||||
description: LogsDedupDescription[dedupType],
|
||||
}))}
|
||||
value={dedupStrategy}
|
||||
onChange={this.onChangeDedup}
|
||||
className={styles.radioButtons}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<div>
|
||||
<InlineField label="Display results" className={styles.horizontalInlineLabel} transparent>
|
||||
<RadioButtonGroup
|
||||
disabled={isFlipping}
|
||||
options={[
|
||||
{
|
||||
label: 'Newest first',
|
||||
value: LogsSortOrder.Descending,
|
||||
description: 'Show results newest to oldest',
|
||||
},
|
||||
{
|
||||
label: 'Oldest first',
|
||||
value: LogsSortOrder.Ascending,
|
||||
description: 'Show results oldest to newest',
|
||||
},
|
||||
]}
|
||||
value={logsSortOrder}
|
||||
onChange={this.onChangeLogsSortOrder}
|
||||
className={styles.radioButtons}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<LogsMetaRow
|
||||
logRows={logRows}
|
||||
meta={logsMeta || []}
|
||||
dedupStrategy={dedupStrategy}
|
||||
dedupCount={dedupCount}
|
||||
hasUnescapedContent={hasUnescapedContent}
|
||||
forceEscape={forceEscape}
|
||||
showDetectedFields={showDetectedFields}
|
||||
onEscapeNewlines={this.onEscapeNewlines}
|
||||
clearDetectedFields={this.clearDetectedFields}
|
||||
/>
|
||||
<div className={styles.logsSection}>
|
||||
<div className={styles.logRows} data-testid="logRows">
|
||||
<LogRows
|
||||
logRows={logRows}
|
||||
deduplicatedRows={dedupedRows}
|
||||
dedupStrategy={dedupStrategy}
|
||||
getRowContext={this.props.getRowContext}
|
||||
onClickFilterLabel={onClickFilterLabel}
|
||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||
showContextToggle={showContextToggle}
|
||||
showLabels={showLabels}
|
||||
showTime={showTime}
|
||||
enableLogDetails={true}
|
||||
forceEscape={forceEscape}
|
||||
wrapLogMessage={wrapLogMessage}
|
||||
prettifyLogMessage={prettifyLogMessage}
|
||||
<Collapse label="Logs volume" collapsible isOpen={logsVolumeEnabled} onToggle={this.onToggleLogsVolumeCollapse}>
|
||||
{logsVolumeEnabled && (
|
||||
<LogsVolumePanel
|
||||
absoluteRange={absoluteRange}
|
||||
width={width}
|
||||
logsVolumeData={logsVolumeData}
|
||||
logLinesBasedData={
|
||||
logsSeries
|
||||
? {
|
||||
data: logsSeries,
|
||||
state: loadingState,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
logLinesBasedDataVisibleRange={visibleRange}
|
||||
onUpdateTimeRange={onChangeTime}
|
||||
timeZone={timeZone}
|
||||
getFieldLinks={getFieldLinks}
|
||||
splitOpen={splitOpen}
|
||||
onLoadLogsVolume={() => loadLogsVolumeData(exploreId)}
|
||||
onHiddenSeriesChanged={this.onToggleLogLevel}
|
||||
/>
|
||||
)}
|
||||
</Collapse>
|
||||
<Collapse label="Logs" loading={loading} isOpen className={styleOverridesForStickyNavigation}>
|
||||
<div className={styles.logOptions} ref={this.topLogsRef}>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Time" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={showTime}
|
||||
onChange={this.onChangeTime}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`show-time_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Unique labels" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={showLabels}
|
||||
onChange={this.onChangeLabels}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`unique-labels_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Wrap lines" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={wrapLogMessage}
|
||||
onChange={this.onChangeWrapLogMessage}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`wrap-lines_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Prettify JSON" className={styles.horizontalInlineLabel} transparent>
|
||||
<InlineSwitch
|
||||
value={prettifyLogMessage}
|
||||
onChange={this.onChangePrettifyLogMessage}
|
||||
className={styles.horizontalInlineSwitch}
|
||||
transparent
|
||||
id={`prettify_${exploreId}`}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField label="Dedup" className={styles.horizontalInlineLabel} transparent>
|
||||
<RadioButtonGroup
|
||||
options={Object.values(LogsDedupStrategy).map((dedupType) => ({
|
||||
label: capitalize(dedupType),
|
||||
value: dedupType,
|
||||
description: LogsDedupDescription[dedupType],
|
||||
}))}
|
||||
value={dedupStrategy}
|
||||
onChange={this.onChangeDedup}
|
||||
className={styles.radioButtons}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<div>
|
||||
<InlineField label="Display results" className={styles.horizontalInlineLabel} transparent>
|
||||
<RadioButtonGroup
|
||||
disabled={isFlipping}
|
||||
options={[
|
||||
{
|
||||
label: 'Newest first',
|
||||
value: LogsSortOrder.Descending,
|
||||
description: 'Show results newest to oldest',
|
||||
},
|
||||
{
|
||||
label: 'Oldest first',
|
||||
value: LogsSortOrder.Ascending,
|
||||
description: 'Show results oldest to newest',
|
||||
},
|
||||
]}
|
||||
value={logsSortOrder}
|
||||
onChange={this.onChangeLogsSortOrder}
|
||||
className={styles.radioButtons}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<LogsMetaRow
|
||||
logRows={logRows}
|
||||
meta={logsMeta || []}
|
||||
dedupStrategy={dedupStrategy}
|
||||
dedupCount={dedupCount}
|
||||
hasUnescapedContent={hasUnescapedContent}
|
||||
forceEscape={forceEscape}
|
||||
showDetectedFields={showDetectedFields}
|
||||
onEscapeNewlines={this.onEscapeNewlines}
|
||||
clearDetectedFields={this.clearDetectedFields}
|
||||
/>
|
||||
<div className={styles.logsSection}>
|
||||
<div className={styles.logRows} data-testid="logRows">
|
||||
<LogRows
|
||||
logRows={logRows}
|
||||
deduplicatedRows={dedupedRows}
|
||||
dedupStrategy={dedupStrategy}
|
||||
getRowContext={this.props.getRowContext}
|
||||
onClickFilterLabel={onClickFilterLabel}
|
||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||
showContextToggle={showContextToggle}
|
||||
showLabels={showLabels}
|
||||
showTime={showTime}
|
||||
enableLogDetails={true}
|
||||
forceEscape={forceEscape}
|
||||
wrapLogMessage={wrapLogMessage}
|
||||
prettifyLogMessage={prettifyLogMessage}
|
||||
timeZone={timeZone}
|
||||
getFieldLinks={getFieldLinks}
|
||||
logsSortOrder={logsSortOrder}
|
||||
showDetectedFields={showDetectedFields}
|
||||
onClickShowDetectedField={this.showDetectedField}
|
||||
onClickHideDetectedField={this.hideDetectedField}
|
||||
/>
|
||||
</div>
|
||||
<LogsNavigation
|
||||
logsSortOrder={logsSortOrder}
|
||||
showDetectedFields={showDetectedFields}
|
||||
onClickShowDetectedField={this.showDetectedField}
|
||||
onClickHideDetectedField={this.hideDetectedField}
|
||||
visibleRange={navigationRange ?? absoluteRange}
|
||||
absoluteRange={absoluteRange}
|
||||
timeZone={timeZone}
|
||||
onChangeTime={onChangeTime}
|
||||
loading={loading}
|
||||
queries={logsQueries ?? []}
|
||||
scrollToTopLogs={this.scrollToTopLogs}
|
||||
addResultsToCache={addResultsToCache}
|
||||
clearCache={clearCache}
|
||||
/>
|
||||
</div>
|
||||
<LogsNavigation
|
||||
logsSortOrder={logsSortOrder}
|
||||
visibleRange={navigationRange ?? absoluteRange}
|
||||
absoluteRange={absoluteRange}
|
||||
timeZone={timeZone}
|
||||
onChangeTime={onChangeTime}
|
||||
loading={loading}
|
||||
queries={logsQueries ?? []}
|
||||
scrollToTopLogs={this.scrollToTopLogs}
|
||||
addResultsToCache={addResultsToCache}
|
||||
clearCache={clearCache}
|
||||
/>
|
||||
</div>
|
||||
{!loading && !hasData && !scanning && (
|
||||
<div className={styles.noData}>
|
||||
No logs found.
|
||||
<Button size="xs" fill="text" onClick={this.onClickScan}>
|
||||
Scan for older logs
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{scanning && (
|
||||
<div className={styles.noData}>
|
||||
<span>{scanText}</span>
|
||||
<Button size="xs" fill="text" onClick={this.onClickStopScan}>
|
||||
Stop scan
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{!loading && !hasData && !scanning && (
|
||||
<div className={styles.noData}>
|
||||
No logs found.
|
||||
<Button size="xs" fill="text" onClick={this.onClickScan}>
|
||||
Scan for older logs
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{scanning && (
|
||||
<div className={styles.noData}>
|
||||
<span>{scanText}</span>
|
||||
<Button size="xs" fill="text" onClick={this.onClickStopScan}>
|
||||
Stop scan
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Collapse>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -508,7 +529,7 @@ const getStyles = (theme: GrafanaTheme2, wrapLogMessage: boolean) => {
|
||||
background-color: ${theme.colors.background.primary};
|
||||
padding: ${theme.spacing(1, 2)};
|
||||
border-radius: ${theme.shape.borderRadius()};
|
||||
margin: ${theme.spacing(2, 0, 1)};
|
||||
margin: ${theme.spacing(0, 0, 1)};
|
||||
border: 1px solid ${theme.colors.border.medium};
|
||||
`,
|
||||
headerButton: css`
|
||||
|
@ -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, setLogsVolumeEnabled } 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<LogsContainerProps> {
|
||||
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 (
|
||||
<>
|
||||
<LogsCrossFadeTransition visible={isLive}>
|
||||
@ -133,37 +122,37 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
</Collapse>
|
||||
</LogsCrossFadeTransition>
|
||||
<LogsCrossFadeTransition visible={!isLive}>
|
||||
<Collapse label="Logs" loading={loading} isOpen className={styleOverridesForStickyNavigation}>
|
||||
<Logs
|
||||
exploreId={exploreId}
|
||||
datasourceType={this.props.datasourceInstance?.type}
|
||||
logRows={logRows}
|
||||
logsMeta={logsMeta}
|
||||
logsSeries={logsSeries}
|
||||
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)}
|
||||
/>
|
||||
</Collapse>
|
||||
<Logs
|
||||
exploreId={exploreId}
|
||||
datasourceType={this.props.datasourceInstance?.type}
|
||||
logRows={logRows}
|
||||
logsMeta={logsMeta}
|
||||
logsSeries={logsSeries}
|
||||
logsVolumeEnabled={this.props.logsVolumeEnabled}
|
||||
onSetLogsVolumeEnabled={(enabled) => this.props.setLogsVolumeEnabled(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)}
|
||||
/>
|
||||
</LogsCrossFadeTransition>
|
||||
</>
|
||||
);
|
||||
@ -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,
|
||||
setLogsVolumeEnabled,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
@ -40,6 +40,7 @@ export const createDefaultInitialState = () => {
|
||||
},
|
||||
cache: [],
|
||||
richHistory: [],
|
||||
logsVolumeEnabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
scanStartAction,
|
||||
scanStopAction,
|
||||
storeLogsVolumeDataProviderAction,
|
||||
setLogsVolumeEnabled,
|
||||
} from './query';
|
||||
import { makeExplorePaneState } from './utils';
|
||||
|
||||
@ -465,5 +466,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(setLogsVolumeEnabled(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(setLogsVolumeEnabled(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(setLogsVolumeEnabled(ExploreId.left, true));
|
||||
|
||||
// verify it was turned on
|
||||
expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(true);
|
||||
|
||||
expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -45,7 +45,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
|
||||
@ -109,6 +109,10 @@ export const queryStoreSubscriptionAction = createAction<QueryStoreSubscriptionP
|
||||
'explore/queryStoreSubscription'
|
||||
);
|
||||
|
||||
const setLogsVolumeEnabledAction = createAction<{ exploreId: ExploreId; enabled: boolean }>(
|
||||
'explore/setLogsVolumeEnabledAction'
|
||||
);
|
||||
|
||||
export interface StoreLogsVolumeDataProvider {
|
||||
exploreId: ExploreId;
|
||||
logsVolumeDataProvider?: Observable<DataQueryResponse>;
|
||||
@ -415,6 +419,7 @@ export const runQueries = (
|
||||
refreshInterval,
|
||||
absoluteRange,
|
||||
cache,
|
||||
logsVolumeEnabled,
|
||||
} = exploreItemState;
|
||||
let newQuerySub;
|
||||
|
||||
@ -550,6 +555,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',
|
||||
@ -564,7 +575,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(
|
||||
@ -670,6 +683,16 @@ export function loadLogsVolumeData(exploreId: ExploreId): ThunkResult<void> {
|
||||
};
|
||||
}
|
||||
|
||||
export function setLogsVolumeEnabled(exploreId: ExploreId, enabled: boolean): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(setLogsVolumeEnabledAction({ exploreId, enabled }));
|
||||
storeLogsVolumeEnabled(enabled);
|
||||
if (enabled) {
|
||||
dispatch(loadLogsVolumeData(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Reducer
|
||||
//
|
||||
@ -757,6 +780,20 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor
|
||||
};
|
||||
}
|
||||
|
||||
if (setLogsVolumeEnabledAction.match(action)) {
|
||||
const { enabled } = action.payload;
|
||||
if (!enabled && 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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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(),
|
||||
|
8
public/app/features/explore/utils/logs.ts
Normal file
8
public/app/features/explore/utils/logs.ts
Normal file
@ -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',
|
||||
};
|
@ -192,6 +192,7 @@ export interface ExploreItemState {
|
||||
|
||||
// properties below should be more generic if we add more providers
|
||||
// see also: DataSourceWithLogsVolumeSupport
|
||||
logsVolumeEnabled: boolean;
|
||||
logsVolumeDataProvider?: Observable<DataQueryResponse>;
|
||||
logsVolumeDataSubscription?: SubscriptionLike;
|
||||
logsVolumeData?: DataQueryResponse;
|
||||
|
Loading…
Reference in New Issue
Block a user