浏览代码

Move deduplication calculation from Logs component to redux selector

Dominik Prokop 6 年之前
父节点
当前提交
1693f083cc

+ 2 - 0
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",

+ 5 - 0
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);

+ 6 - 10
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<LogLevel>;
   onChangeTime?: (range: RawTimeRange) => void;
   onClickLabel?: (label: string, value: string) => void;
   onStartScanning?: () => void;
   onStopScanning?: () => void;
   onDedupStrategyChange: (dedupStrategy: LogsDedupStrategy) => void;
+  onToggleLogLevel: (hiddenLogLevels: Set<LogLevel>) => void;
 }
 
 interface State {
   deferLogs: boolean;
-  hiddenLogLevels: Set<LogLevel>;
   renderAll: boolean;
   showLabels: boolean | null; // Tristate: null means auto
   showLocalTime: boolean;
@@ -81,7 +81,6 @@ export default class Logs extends PureComponent<Props, State> {
 
   state = {
     deferLogs: true,
-    hiddenLogLevels: new Set(),
     renderAll: false,
     showLabels: null,
     showLocalTime: true,
@@ -142,7 +141,7 @@ export default class Logs extends PureComponent<Props, State> {
 
   onToggleLogLevel = (rawLevel: string, hiddenRawLevels: Set<string>) => {
     const hiddenLogLevels: Set<LogLevel> = 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<Props, State> {
       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];
 

+ 40 - 2
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<LogLevel>;
   width: number;
 }
 
@@ -39,12 +44,21 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
     this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy);
   };
 
+  hangleToggleLogLevel = (hiddenLogLevels: Set<LogLevel>) => {
+    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<LogsContainerProps> {
       scanning,
       scanRange,
       width,
+      hiddenLogLevels,
     } = this.props;
 
     return (
@@ -61,6 +76,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
         <Logs
           dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
           data={logsResult}
+          dedupedData={dedupedResult}
           exploreId={exploreId}
           key={logsResult && logsResult.id}
           highlighterExpressions={logsHighlighterExpressions}
@@ -70,10 +86,12 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
           onStartScanning={onStartScanning}
           onStopScanning={onStopScanning}
           onDedupStrategyChange={this.handleDedupStrategyChange}
+          onToggleLogLevel={this.hangleToggleLogLevel}
           range={range}
           scanning={scanning}
           scanRange={scanRange}
           width={width}
+          hiddenLogLevels={hiddenLogLevels}
         />
       </Panel>
     );
@@ -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));

+ 12 - 1
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<LogLevel>;
+}
+
 export interface QueriesImportedPayload {
   exploreId: ExploreId;
   queries: DataQuery[];
@@ -397,6 +403,10 @@ export const updateDatasourceInstanceAction = actionCreatorFactory<UpdateDatasou
   'explore/UPDATE_DATASOURCE_INSTANCE'
 ).create();
 
+export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>(
+  'explore/TOGGLE_LOG_LEVEL'
+).create();
+
 /**
  * Resets state for explore.
  */
@@ -436,4 +446,5 @@ export type Action =
   | ActionOf<ToggleGraphPayload>
   | ActionOf<ToggleLogsPayload>
   | ActionOf<UpdateDatasourceInstancePayload>
-  | ActionOf<QueriesImportedPayload>;
+  | ActionOf<QueriesImportedPayload>
+  | ActionOf<ToggleLogLevelPayload>;

+ 11 - 0
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<ExploreItemState>({} as ExploreItemSta
       };
     },
   })
+  .addMapper({
+    filter: toggleLogLevelAction,
+    mapper: (state, action): ExploreItemState => {
+      const { hiddenLogLevels } = action.payload;
+      return {
+        ...state,
+        hiddenLogLevels: Array.from(hiddenLogLevels)
+      };
+    },
+  })
   .create();
 
 /**

+ 6 - 1
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 {

+ 12 - 0
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"