From 1693f083cc8e35efaa9daf9bc8398c9d2c70b525 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 11 Feb 2019 16:57:49 +0100 Subject: [PATCH] Move deduplication calculation from Logs component to redux selector --- package.json | 2 + public/app/core/utils/reselect.ts | 5 +++ public/app/features/explore/Logs.tsx | 16 +++---- public/app/features/explore/LogsContainer.tsx | 42 ++++++++++++++++++- .../app/features/explore/state/actionTypes.ts | 13 +++++- public/app/features/explore/state/reducers.ts | 11 +++++ public/app/types/explore.ts | 7 +++- yarn.lock | 12 ++++++ 8 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 public/app/core/utils/reselect.ts diff --git a/package.json b/package.json index fae51a1d856..22cfe33a4d0 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,7 @@ "dependencies": { "@babel/polyfill": "^7.0.0", "@torkelo/react-select": "2.1.1", + "@types/reselect": "^2.2.0", "angular": "1.6.6", "angular-bindonce": "0.3.1", "angular-native-dragdrop": "1.2.2", @@ -187,6 +188,7 @@ "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", "remarkable": "^1.7.1", + "reselect": "^4.0.0", "rst2html": "github:thoward/rst2html#990cb89", "rxjs": "^6.3.3", "slate": "^0.33.4", diff --git a/public/app/core/utils/reselect.ts b/public/app/core/utils/reselect.ts new file mode 100644 index 00000000000..7c8fc7727b0 --- /dev/null +++ b/public/app/core/utils/reselect.ts @@ -0,0 +1,5 @@ +import { memoize } from 'lodash'; +import { createSelectorCreator } from 'reselect'; + +const hashFn = (...args) => args.reduce((acc, val) => acc + '-' + JSON.stringify(val), ''); +export const createLodashMemoizedSelector = createSelectorCreator(memoize, hashFn); diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index f41555b9121..0e3b3f3558e 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -9,8 +9,6 @@ import { LogsDedupDescription, LogsDedupStrategy, LogsModel, - dedupLogRows, - filterLogLevels, LogLevel, LogsMetaKind, } from 'app/core/logs_model'; @@ -51,6 +49,7 @@ function renderMetaItem(value: any, kind: LogsMetaKind) { interface Props { data?: LogsModel; + dedupedData?: LogsModel; width: number; exploreId: string; highlighterExpressions: string[]; @@ -59,16 +58,17 @@ interface Props { scanning?: boolean; scanRange?: RawTimeRange; dedupStrategy: LogsDedupStrategy; + hiddenLogLevels: Set; onChangeTime?: (range: RawTimeRange) => void; onClickLabel?: (label: string, value: string) => void; onStartScanning?: () => void; onStopScanning?: () => void; onDedupStrategyChange: (dedupStrategy: LogsDedupStrategy) => void; + onToggleLogLevel: (hiddenLogLevels: Set) => void; } interface State { deferLogs: boolean; - hiddenLogLevels: Set; renderAll: boolean; showLabels: boolean | null; // Tristate: null means auto showLocalTime: boolean; @@ -81,7 +81,6 @@ export default class Logs extends PureComponent { state = { deferLogs: true, - hiddenLogLevels: new Set(), renderAll: false, showLabels: null, showLocalTime: true, @@ -142,7 +141,7 @@ export default class Logs extends PureComponent { onToggleLogLevel = (rawLevel: string, hiddenRawLevels: Set) => { const hiddenLogLevels: Set = new Set(Array.from(hiddenRawLevels).map(level => LogLevel[level])); - this.setState({ hiddenLogLevels }); + this.props.onToggleLogLevel(hiddenLogLevels); }; onClickScan = (event: React.SyntheticEvent) => { @@ -166,21 +165,18 @@ export default class Logs extends PureComponent { scanning, scanRange, width, + dedupedData, } = this.props; if (!data) { return null; } - const { deferLogs, hiddenLogLevels, renderAll, showLocalTime, showUtc, } = this.state; + const { deferLogs, renderAll, showLocalTime, showUtc } = this.state; let { showLabels } = this.state; const { dedupStrategy } = this.props; const hasData = data && data.rows && data.rows.length > 0; const showDuplicates = dedupStrategy !== LogsDedupStrategy.none; - - // Filtering - const filteredData = filterLogLevels(data, hiddenLogLevels); - const dedupedData = dedupLogRows(filteredData, dedupStrategy); const dedupCount = dedupedData.rows.reduce((sum, row) => sum + row.duplicates, 0); const meta = [...data.meta]; diff --git a/public/app/features/explore/LogsContainer.tsx b/public/app/features/explore/LogsContainer.tsx index 190c1c43b5a..9fd06afae9b 100644 --- a/public/app/features/explore/LogsContainer.tsx +++ b/public/app/features/explore/LogsContainer.tsx @@ -4,18 +4,21 @@ import { connect } from 'react-redux'; import { RawTimeRange, TimeRange } from '@grafana/ui'; import { ExploreId, ExploreItemState } from 'app/types/explore'; -import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model'; +import { LogsModel, LogsDedupStrategy, LogLevel, filterLogLevels, dedupLogRows } from 'app/core/logs_model'; import { StoreState } from 'app/types'; import { toggleLogs, changeDedupStrategy } from './state/actions'; import Logs from './Logs'; import Panel from './Panel'; +import { toggleLogLevelAction } from 'app/features/explore/state/actionTypes'; +import { createLodashMemoizedSelector } from 'app/core/utils/reselect'; interface LogsContainerProps { exploreId: ExploreId; loading: boolean; logsHighlighterExpressions?: string[]; logsResult?: LogsModel; + dedupedResult?: LogsModel; onChangeTime: (range: TimeRange) => void; onClickLabel: (key: string, value: string) => void; onStartScanning: () => void; @@ -25,8 +28,10 @@ interface LogsContainerProps { scanRange?: RawTimeRange; showingLogs: boolean; toggleLogs: typeof toggleLogs; + toggleLogLevelAction: typeof toggleLogLevelAction; changeDedupStrategy: typeof changeDedupStrategy; dedupStrategy: LogsDedupStrategy; + hiddenLogLevels: Set; width: number; } @@ -39,12 +44,21 @@ export class LogsContainer extends PureComponent { this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy); }; + hangleToggleLogLevel = (hiddenLogLevels: Set) => { + const { exploreId } = this.props; + this.props.toggleLogLevelAction({ + exploreId, + hiddenLogLevels, + }); + }; + render() { const { exploreId, loading, logsHighlighterExpressions, logsResult, + dedupedResult, onChangeTime, onClickLabel, onStartScanning, @@ -54,6 +68,7 @@ export class LogsContainer extends PureComponent { scanning, scanRange, width, + hiddenLogLevels, } = this.props; return ( @@ -61,6 +76,7 @@ export class LogsContainer extends PureComponent { { onStartScanning={onStartScanning} onStopScanning={onStopScanning} onDedupStrategyChange={this.handleDedupStrategyChange} + onToggleLogLevel={this.hangleToggleLogLevel} range={range} scanning={scanning} scanRange={scanRange} width={width} + hiddenLogLevels={hiddenLogLevels} /> ); @@ -90,12 +108,29 @@ const selectItemUIState = (itemState: ExploreItemState) => { dedupStrategy, }; }; + +const logsSelector = (state: ExploreItemState) => state.logsResult; +const hiddenLogLevelsSelector = (state: ExploreItemState) => state.hiddenLogLevels; +const dedupStrategySelector = (state: ExploreItemState) => state.dedupStrategy; +const deduplicatedLogsSelector = createLodashMemoizedSelector( + logsSelector, hiddenLogLevelsSelector, dedupStrategySelector, + (logs, hiddenLogLevels, dedupStrategy) => { + if (!logs) { + return null; + } + const filteredData = filterLogLevels(logs, new Set(hiddenLogLevels)); + return dedupLogRows(filteredData, dedupStrategy); + } +); + function mapStateToProps(state: StoreState, { exploreId }) { const explore = state.explore; const item: ExploreItemState = explore[exploreId]; const { logsHighlighterExpressions, logsResult, queryTransactions, scanning, scanRange, range } = item; const loading = queryTransactions.some(qt => qt.resultType === 'Logs' && !qt.done); - const {showingLogs, dedupStrategy} = selectItemUIState(item); + const { showingLogs, dedupStrategy } = selectItemUIState(item); + const hiddenLogLevels = new Set(item.hiddenLogLevels); + const dedupedResult = deduplicatedLogsSelector(item); return { loading, @@ -106,12 +141,15 @@ function mapStateToProps(state: StoreState, { exploreId }) { showingLogs, range, dedupStrategy, + hiddenLogLevels, + dedupedResult, }; } const mapDispatchToProps = { toggleLogs, changeDedupStrategy, + toggleLogLevelAction, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(LogsContainer)); diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts index d54a8754c3d..c54eef97a43 100644 --- a/public/app/features/explore/state/actionTypes.ts +++ b/public/app/features/explore/state/actionTypes.ts @@ -18,6 +18,7 @@ import { ExploreUIState, } from 'app/types/explore'; import { actionCreatorFactory, noPayloadActionCreatorFactory, ActionOf } from 'app/core/redux/actionCreatorFactory'; +import { LogLevel } from 'app/core/logs_model'; /** Higher order actions * @@ -201,6 +202,11 @@ export interface UpdateDatasourceInstancePayload { datasourceInstance: DataSourceApi; } +export interface ToggleLogLevelPayload { + exploreId: ExploreId; + hiddenLogLevels: Set; +} + export interface QueriesImportedPayload { exploreId: ExploreId; queries: DataQuery[]; @@ -397,6 +403,10 @@ export const updateDatasourceInstanceAction = actionCreatorFactory( + 'explore/TOGGLE_LOG_LEVEL' +).create(); + /** * Resets state for explore. */ @@ -436,4 +446,5 @@ export type Action = | ActionOf | ActionOf | ActionOf - | ActionOf; + | ActionOf + | ActionOf; diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 255591ee6e3..db3e9a95858 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -38,6 +38,7 @@ import { toggleTableAction, queriesImportedAction, updateUIStateAction, + toggleLogLevelAction, } from './actionTypes'; export const DEFAULT_RANGE = { @@ -467,6 +468,16 @@ export const itemReducer = reducerFactory({} as ExploreItemSta }; }, }) + .addMapper({ + filter: toggleLogLevelAction, + mapper: (state, action): ExploreItemState => { + const { hiddenLogLevels } = action.payload; + return { + ...state, + hiddenLogLevels: Array.from(hiddenLogLevels) + }; + }, + }) .create(); /** diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 066ca226157..7a6af04b2ee 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -11,7 +11,7 @@ import { } from '@grafana/ui'; import { Emitter } from 'app/core/core'; -import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model'; +import { LogsModel, LogsDedupStrategy, LogLevel } from 'app/core/logs_model'; import TableModel from 'app/core/table_model'; export interface CompletionItem { @@ -242,6 +242,11 @@ export interface ExploreItemState { * Current logs deduplication strategy */ dedupStrategy?: LogsDedupStrategy; + + /** + * Currently hidden log series + */ + hiddenLogLevels?: LogLevel[]; } export interface ExploreUIState { diff --git a/yarn.lock b/yarn.lock index df2e1cea37e..3c86bdf810f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1819,6 +1819,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/reselect@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/reselect/-/reselect-2.2.0.tgz#c667206cfdc38190e1d379babe08865b2288575f" + integrity sha1-xmcgbP3DgZDh03m6vgiGWyKIV18= + dependencies: + reselect "*" + "@types/storybook__addon-actions@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@types/storybook__addon-actions/-/storybook__addon-actions-3.4.1.tgz#8f90d76b023b58ee794170f2fe774a3fddda2c1d" @@ -14894,6 +14901,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +reselect@*, reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"