mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #15306 from grafana/explore/dedup-strategu-url
Persist deduplication strategy in url
This commit is contained in:
commit
a79bd42414
@ -8,6 +8,7 @@ import {
|
||||
} from './explore';
|
||||
import { ExploreUrlState } from 'app/types/explore';
|
||||
import store from 'app/core/store';
|
||||
import { LogsDedupStrategy } from 'app/core/logs_model';
|
||||
|
||||
const DEFAULT_EXPLORE_STATE: ExploreUrlState = {
|
||||
datasource: null,
|
||||
@ -17,6 +18,7 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = {
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
showingLogs: true,
|
||||
dedupStrategy: LogsDedupStrategy.none,
|
||||
}
|
||||
};
|
||||
|
||||
@ -78,7 +80,7 @@ describe('state functions', () => {
|
||||
expect(serializeStateToUrlParam(state)).toBe(
|
||||
'{"datasource":"foo","queries":[{"expr":"metric{test=\\"a/b\\"}"},' +
|
||||
'{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"},' +
|
||||
'"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}'
|
||||
'"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true,"dedupStrategy":"none"}}'
|
||||
);
|
||||
});
|
||||
|
||||
@ -100,7 +102,7 @@ describe('state functions', () => {
|
||||
},
|
||||
};
|
||||
expect(serializeStateToUrlParam(state, true)).toBe(
|
||||
'["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true]}]'
|
||||
'["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true,"none"]}]'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
QueryIntervals,
|
||||
QueryOptions,
|
||||
} from 'app/types/explore';
|
||||
import { LogsDedupStrategy } from 'app/core/logs_model';
|
||||
|
||||
export const DEFAULT_RANGE = {
|
||||
from: 'now-6h',
|
||||
@ -31,6 +32,7 @@ export const DEFAULT_UI_STATE = {
|
||||
showingTable: true,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
dedupStrategy: LogsDedupStrategy.none,
|
||||
};
|
||||
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
@ -183,6 +185,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||
showingGraph: segment.ui[0],
|
||||
showingLogs: segment.ui[1],
|
||||
showingTable: segment.ui[2],
|
||||
dedupStrategy: segment.ui[3],
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -204,7 +207,7 @@ export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: bo
|
||||
urlState.range.to,
|
||||
urlState.datasource,
|
||||
...urlState.queries,
|
||||
{ ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable] },
|
||||
{ ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable, urlState.ui.dedupStrategy] },
|
||||
]);
|
||||
}
|
||||
return JSON.stringify(urlState);
|
||||
|
@ -25,7 +25,7 @@ interface GraphContainerProps {
|
||||
|
||||
export class GraphContainer extends PureComponent<GraphContainerProps> {
|
||||
onClickGraphButton = () => {
|
||||
this.props.toggleGraph(this.props.exploreId);
|
||||
this.props.toggleGraph(this.props.exploreId, this.props.showingGraph);
|
||||
};
|
||||
|
||||
onChangeTime = (timeRange: TimeRange) => {
|
||||
|
@ -58,14 +58,15 @@ interface Props {
|
||||
range?: RawTimeRange;
|
||||
scanning?: boolean;
|
||||
scanRange?: RawTimeRange;
|
||||
dedupStrategy: LogsDedupStrategy;
|
||||
onChangeTime?: (range: RawTimeRange) => void;
|
||||
onClickLabel?: (label: string, value: string) => void;
|
||||
onStartScanning?: () => void;
|
||||
onStopScanning?: () => void;
|
||||
onDedupStrategyChange: (dedupStrategy: LogsDedupStrategy) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
dedup: LogsDedupStrategy;
|
||||
deferLogs: boolean;
|
||||
hiddenLogLevels: Set<LogLevel>;
|
||||
renderAll: boolean;
|
||||
@ -79,7 +80,6 @@ export default class Logs extends PureComponent<Props, State> {
|
||||
renderAllTimer: NodeJS.Timer;
|
||||
|
||||
state = {
|
||||
dedup: LogsDedupStrategy.none,
|
||||
deferLogs: true,
|
||||
hiddenLogLevels: new Set(),
|
||||
renderAll: false,
|
||||
@ -112,12 +112,11 @@ export default class Logs extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
onChangeDedup = (dedup: LogsDedupStrategy) => {
|
||||
this.setState(prevState => {
|
||||
if (prevState.dedup === dedup) {
|
||||
return { dedup: LogsDedupStrategy.none };
|
||||
}
|
||||
return { dedup };
|
||||
});
|
||||
const { onDedupStrategyChange } = this.props;
|
||||
if (this.props.dedupStrategy === dedup) {
|
||||
return onDedupStrategyChange(LogsDedupStrategy.none);
|
||||
}
|
||||
return onDedupStrategyChange(dedup);
|
||||
};
|
||||
|
||||
onChangeLabels = (event: React.SyntheticEvent) => {
|
||||
@ -173,17 +172,19 @@ export default class Logs extends PureComponent<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { dedup, deferLogs, hiddenLogLevels, renderAll, showLocalTime, showUtc } = this.state;
|
||||
const { deferLogs, hiddenLogLevels, renderAll, showLocalTime, showUtc, } = this.state;
|
||||
let { showLabels } = this.state;
|
||||
const { dedupStrategy } = this.props;
|
||||
const hasData = data && data.rows && data.rows.length > 0;
|
||||
const showDuplicates = dedup !== LogsDedupStrategy.none;
|
||||
const showDuplicates = dedupStrategy !== LogsDedupStrategy.none;
|
||||
|
||||
// Filtering
|
||||
const filteredData = filterLogLevels(data, hiddenLogLevels);
|
||||
const dedupedData = dedupLogRows(filteredData, dedup);
|
||||
const dedupedData = dedupLogRows(filteredData, dedupStrategy);
|
||||
const dedupCount = dedupedData.rows.reduce((sum, row) => sum + row.duplicates, 0);
|
||||
const meta = [...data.meta];
|
||||
if (dedup !== LogsDedupStrategy.none) {
|
||||
|
||||
if (dedupStrategy !== LogsDedupStrategy.none) {
|
||||
meta.push({
|
||||
label: 'Dedup count',
|
||||
value: dedupCount,
|
||||
@ -236,7 +237,7 @@ export default class Logs extends PureComponent<Props, State> {
|
||||
key={i}
|
||||
value={dedupType}
|
||||
onChange={this.onChangeDedup}
|
||||
selected={dedup === dedupType}
|
||||
selected={dedupStrategy === dedupType}
|
||||
tooltip={LogsDedupDescription[dedupType]}
|
||||
>
|
||||
{dedupType}
|
||||
|
@ -4,10 +4,10 @@ import { connect } from 'react-redux';
|
||||
import { RawTimeRange, TimeRange } from '@grafana/ui';
|
||||
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { LogsModel } from 'app/core/logs_model';
|
||||
import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { toggleLogs } from './state/actions';
|
||||
import { toggleLogs, changeDedupStrategy } from './state/actions';
|
||||
import Logs from './Logs';
|
||||
import Panel from './Panel';
|
||||
|
||||
@ -25,12 +25,18 @@ interface LogsContainerProps {
|
||||
scanRange?: RawTimeRange;
|
||||
showingLogs: boolean;
|
||||
toggleLogs: typeof toggleLogs;
|
||||
changeDedupStrategy: typeof changeDedupStrategy;
|
||||
dedupStrategy: LogsDedupStrategy;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
onClickLogsButton = () => {
|
||||
this.props.toggleLogs(this.props.exploreId);
|
||||
this.props.toggleLogs(this.props.exploreId, this.props.showingLogs);
|
||||
};
|
||||
|
||||
handleDedupStrategyChange = (dedupStrategy: LogsDedupStrategy) => {
|
||||
this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy);
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -53,6 +59,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
return (
|
||||
<Panel label="Logs" loading={loading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
|
||||
<Logs
|
||||
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
|
||||
data={logsResult}
|
||||
exploreId={exploreId}
|
||||
key={logsResult && logsResult.id}
|
||||
@ -62,6 +69,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
onClickLabel={onClickLabel}
|
||||
onStartScanning={onStartScanning}
|
||||
onStopScanning={onStopScanning}
|
||||
onDedupStrategyChange={this.handleDedupStrategyChange}
|
||||
range={range}
|
||||
scanning={scanning}
|
||||
scanRange={scanRange}
|
||||
@ -72,11 +80,23 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
}
|
||||
}
|
||||
|
||||
const selectItemUIState = (itemState: ExploreItemState) => {
|
||||
const { showingGraph, showingLogs, showingTable, showingStartPage, dedupStrategy } = itemState;
|
||||
return {
|
||||
showingGraph,
|
||||
showingLogs,
|
||||
showingTable,
|
||||
showingStartPage,
|
||||
dedupStrategy,
|
||||
};
|
||||
};
|
||||
function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const { logsHighlighterExpressions, logsResult, queryTransactions, scanning, scanRange, showingLogs, range } = item;
|
||||
const { logsHighlighterExpressions, logsResult, queryTransactions, scanning, scanRange, range } = item;
|
||||
const loading = queryTransactions.some(qt => qt.resultType === 'Logs' && !qt.done);
|
||||
const {showingLogs, dedupStrategy} = selectItemUIState(item);
|
||||
|
||||
return {
|
||||
loading,
|
||||
logsHighlighterExpressions,
|
||||
@ -85,11 +105,13 @@ function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
scanRange,
|
||||
showingLogs,
|
||||
range,
|
||||
dedupStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleLogs,
|
||||
changeDedupStrategy,
|
||||
};
|
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(LogsContainer));
|
||||
|
@ -21,7 +21,7 @@ interface TableContainerProps {
|
||||
|
||||
export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
onClickTableButton = () => {
|
||||
this.props.toggleTable(this.props.exploreId);
|
||||
this.props.toggleTable(this.props.exploreId, this.props.showingTable);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -192,6 +192,10 @@ export interface ToggleLogsPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface UpdateUIStatePayload extends Partial<ExploreUIState>{
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface UpdateDatasourceInstancePayload {
|
||||
exploreId: ExploreId;
|
||||
datasourceInstance: DataSourceApi;
|
||||
@ -366,6 +370,11 @@ export const splitCloseAction = noPayloadActionCreatorFactory('explore/SPLIT_CLO
|
||||
export const splitOpenAction = actionCreatorFactory<SplitOpenPayload>('explore/SPLIT_OPEN').create();
|
||||
export const stateSaveAction = noPayloadActionCreatorFactory('explore/STATE_SAVE').create();
|
||||
|
||||
/**
|
||||
* Update state of Explores UI elements (panels visiblity and deduplication strategy)
|
||||
*/
|
||||
export const updateUIStateAction = actionCreatorFactory<UpdateUIStatePayload>('explore/UPDATE_UI_STATE').create();
|
||||
|
||||
/**
|
||||
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
|
||||
*/
|
||||
|
@ -67,14 +67,26 @@ import {
|
||||
ToggleGraphPayload,
|
||||
ToggleLogsPayload,
|
||||
ToggleTablePayload,
|
||||
updateUIStateAction,
|
||||
} from './actionTypes';
|
||||
import { ActionOf, ActionCreator } from 'app/core/redux/actionCreatorFactory';
|
||||
import { LogsDedupStrategy } from 'app/core/logs_model';
|
||||
|
||||
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
|
||||
|
||||
// /**
|
||||
// * Adds a query row after the row with the given index.
|
||||
// */
|
||||
/**
|
||||
* Updates UI state and save it to the URL
|
||||
*/
|
||||
const updateExploreUIState = (exploreId, uiStateFragment: Partial<ExploreUIState>) => {
|
||||
return dispatch => {
|
||||
dispatch(updateUIStateAction({ exploreId, ...uiStateFragment }));
|
||||
dispatch(stateSave());
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a query row after the row with the given index.
|
||||
*/
|
||||
export function addQueryRow(exploreId: ExploreId, index: number): ActionOf<AddQueryRowPayload> {
|
||||
const query = generateEmptyQuery(index + 1);
|
||||
return addQueryRowAction({ exploreId, index, query });
|
||||
@ -669,6 +681,7 @@ export function stateSave() {
|
||||
showingGraph: left.showingGraph,
|
||||
showingLogs: left.showingLogs,
|
||||
showingTable: left.showingTable,
|
||||
dedupStrategy: left.dedupStrategy,
|
||||
},
|
||||
};
|
||||
urlStates.left = serializeStateToUrlParam(leftUrlState, true);
|
||||
@ -677,7 +690,12 @@ export function stateSave() {
|
||||
datasource: right.datasourceInstance.name,
|
||||
queries: right.queries.map(clearQueryKeys),
|
||||
range: right.range,
|
||||
ui: { showingGraph: right.showingGraph, showingLogs: right.showingLogs, showingTable: right.showingTable },
|
||||
ui: {
|
||||
showingGraph: right.showingGraph,
|
||||
showingLogs: right.showingLogs,
|
||||
showingTable: right.showingTable,
|
||||
dedupStrategy: right.dedupStrategy,
|
||||
},
|
||||
};
|
||||
|
||||
urlStates.right = serializeStateToUrlParam(rightUrlState, true);
|
||||
@ -696,24 +714,26 @@ const togglePanelActionCreator = (
|
||||
| ActionCreator<ToggleGraphPayload>
|
||||
| ActionCreator<ToggleLogsPayload>
|
||||
| ActionCreator<ToggleTablePayload>
|
||||
) => (exploreId: ExploreId) => {
|
||||
return (dispatch, getState) => {
|
||||
let shouldRunQueries;
|
||||
dispatch(actionCreator({ exploreId }));
|
||||
dispatch(stateSave());
|
||||
) => (exploreId: ExploreId, isPanelVisible: boolean) => {
|
||||
return dispatch => {
|
||||
let uiFragmentStateUpdate: Partial<ExploreUIState>;
|
||||
const shouldRunQueries = !isPanelVisible;
|
||||
|
||||
switch (actionCreator.type) {
|
||||
case toggleGraphAction.type:
|
||||
shouldRunQueries = getState().explore[exploreId].showingGraph;
|
||||
uiFragmentStateUpdate = { showingGraph: !isPanelVisible };
|
||||
break;
|
||||
case toggleLogsAction.type:
|
||||
shouldRunQueries = getState().explore[exploreId].showingLogs;
|
||||
uiFragmentStateUpdate = { showingLogs: !isPanelVisible };
|
||||
break;
|
||||
case toggleTableAction.type:
|
||||
shouldRunQueries = getState().explore[exploreId].showingTable;
|
||||
uiFragmentStateUpdate = { showingTable: !isPanelVisible };
|
||||
break;
|
||||
}
|
||||
|
||||
dispatch(actionCreator({ exploreId }));
|
||||
dispatch(updateExploreUIState(exploreId, uiFragmentStateUpdate));
|
||||
|
||||
if (shouldRunQueries) {
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
@ -734,3 +754,12 @@ export const toggleLogs = togglePanelActionCreator(toggleLogsAction);
|
||||
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
|
||||
*/
|
||||
export const toggleTable = togglePanelActionCreator(toggleTableAction);
|
||||
|
||||
/**
|
||||
* Change logs deduplication strategy and update URL.
|
||||
*/
|
||||
export const changeDedupStrategy = (exploreId, dedupStrategy: LogsDedupStrategy) => {
|
||||
return dispatch => {
|
||||
dispatch(updateExploreUIState(exploreId, { dedupStrategy }));
|
||||
};
|
||||
};
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
toggleLogsAction,
|
||||
toggleTableAction,
|
||||
queriesImportedAction,
|
||||
updateUIStateAction,
|
||||
} from './actionTypes';
|
||||
|
||||
export const DEFAULT_RANGE = {
|
||||
@ -406,6 +407,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: updateUIStateAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: toggleGraphAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
@ -415,7 +422,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
// Discard transactions related to Graph query
|
||||
nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Graph');
|
||||
}
|
||||
return { ...state, queryTransactions: nextQueryTransactions, showingGraph };
|
||||
return { ...state, queryTransactions: nextQueryTransactions };
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
@ -427,7 +434,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
// Discard transactions related to Logs query
|
||||
nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Logs');
|
||||
}
|
||||
return { ...state, queryTransactions: nextQueryTransactions, showingLogs };
|
||||
return { ...state, queryTransactions: nextQueryTransactions };
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
@ -435,7 +442,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
mapper: (state): ExploreItemState => {
|
||||
const showingTable = !state.showingTable;
|
||||
if (showingTable) {
|
||||
return { ...state, showingTable, queryTransactions: state.queryTransactions };
|
||||
return { ...state, queryTransactions: state.queryTransactions };
|
||||
}
|
||||
|
||||
// Toggle off needs discarding of table queries and results
|
||||
@ -446,7 +453,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
state.queryIntervals.intervalMs
|
||||
);
|
||||
|
||||
return { ...state, ...results, queryTransactions: nextQueryTransactions, showingTable };
|
||||
return { ...state, ...results, queryTransactions: nextQueryTransactions };
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
|
@ -1,6 +1,9 @@
|
||||
jest.mock('app/core/core', () => ({}));
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
bootData: {
|
||||
user: {},
|
||||
},
|
||||
panels: {
|
||||
test: {
|
||||
id: 'test',
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from '@grafana/ui';
|
||||
|
||||
import { Emitter } from 'app/core/core';
|
||||
import { LogsModel } from 'app/core/logs_model';
|
||||
import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model';
|
||||
import TableModel from 'app/core/table_model';
|
||||
|
||||
export interface CompletionItem {
|
||||
@ -237,12 +237,18 @@ export interface ExploreItemState {
|
||||
* React keys for rendering of QueryRows
|
||||
*/
|
||||
queryKeys: string[];
|
||||
|
||||
/**
|
||||
* Current logs deduplication strategy
|
||||
*/
|
||||
dedupStrategy?: LogsDedupStrategy;
|
||||
}
|
||||
|
||||
export interface ExploreUIState {
|
||||
showingTable: boolean;
|
||||
showingGraph: boolean;
|
||||
showingLogs: boolean;
|
||||
dedupStrategy?: LogsDedupStrategy;
|
||||
}
|
||||
|
||||
export interface ExploreUrlState {
|
||||
|
Loading…
Reference in New Issue
Block a user