refs #13828 Merge branch 'origin/13828-trash-view'
[arvados-workbench2.git] / src / store / process-logs-panel / process-logs-panel-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { unionize, ofType, UnionOf } from "~/common/unionize";
6 import { ProcessLogs, getProcessLogsPanelCurrentUuid } from './process-logs-panel';
7 import { LogEventType } from '~/models/log';
8 import { RootState } from '~/store/store';
9 import { ServiceRepository } from '~/services/services';
10 import { Dispatch } from 'redux';
11 import { groupBy } from 'lodash';
12 import { loadProcess } from '~/store/processes/processes-actions';
13 import { LogResource } from '~/models/log';
14 import { LogService } from '~/services/log-service/log-service';
15 import { ResourceEventMessage } from '~/websocket/resource-event-message';
16 import { getProcess } from '~/store/processes/process';
17 import { FilterBuilder } from "~/services/api/filter-builder";
18 import { OrderBuilder } from "~/services/api/order-builder";
19
20 export const processLogsPanelActions = unionize({
21     RESET_PROCESS_LOGS_PANEL: ofType<{}>(),
22     INIT_PROCESS_LOGS_PANEL: ofType<{ filters: string[], logs: ProcessLogs }>(),
23     SET_PROCESS_LOGS_PANEL_FILTER: ofType<string>(),
24     ADD_PROCESS_LOGS_PANEL_ITEM: ofType<{ logType: string, log: string }>(),
25 });
26
27 export type ProcessLogsPanelAction = UnionOf<typeof processLogsPanelActions>;
28
29 export const setProcessLogsPanelFilter = (filter: string) =>
30     processLogsPanelActions.SET_PROCESS_LOGS_PANEL_FILTER(filter);
31
32 export const initProcessLogsPanel = (processUuid: string) =>
33     async (dispatch: Dispatch, getState: () => RootState, { logService }: ServiceRepository) => {
34         dispatch(processLogsPanelActions.RESET_PROCESS_LOGS_PANEL());
35         const process = await dispatch<any>(loadProcess(processUuid));
36         if (process.container) {
37             const logResources = await loadContainerLogs(process.container.uuid, logService);
38             const initialState = createInitialLogPanelState(logResources);
39             dispatch(processLogsPanelActions.INIT_PROCESS_LOGS_PANEL(initialState));
40         }
41     };
42
43 export const addProcessLogsPanelItem = (message: ResourceEventMessage<{ text: string }>) =>
44     async (dispatch: Dispatch, getState: () => RootState, { logService }: ServiceRepository) => {
45         if (PROCESS_PANEL_LOG_EVENT_TYPES.indexOf(message.eventType) > -1) {
46             const uuid = getProcessLogsPanelCurrentUuid(getState());
47             if (uuid) {
48                 const process = getProcess(uuid)(getState().resources);
49                 if (process) {
50                     const { containerRequest, container } = process;
51                     if (message.objectUuid === containerRequest.uuid
52                         || container && message.objectUuid === container.uuid) {
53                         dispatch(processLogsPanelActions.ADD_PROCESS_LOGS_PANEL_ITEM({
54                             logType: SUMMARIZED_FILTER_TYPE,
55                             log: message.properties.text
56                         }));
57                         dispatch(processLogsPanelActions.ADD_PROCESS_LOGS_PANEL_ITEM({
58                             logType: message.eventType,
59                             log: message.properties.text
60                         }));
61                     }
62                 }
63             }
64         }
65     };
66
67 const loadContainerLogs = async (containerUuid: string, logService: LogService) => {
68     const requestFilters = new FilterBuilder()
69         .addEqual('objectUuid', containerUuid)
70         .addIn('eventType', PROCESS_PANEL_LOG_EVENT_TYPES)
71         .getFilters();
72     const requestOrder = new OrderBuilder<LogResource>()
73         .addAsc('eventAt')
74         .getOrder();
75     const requestParams = {
76         limit: MAX_AMOUNT_OF_LOGS,
77         filters: requestFilters,
78         order: requestOrder,
79     };
80     const { items } = await logService.list(requestParams);
81     return items;
82 };
83
84 const createInitialLogPanelState = (logResources: LogResource[]) => {
85     const allLogs = logsToLines(logResources);
86     const groupedLogResources = groupBy(logResources, log => log.eventType);
87     const groupedLogs = Object
88         .keys(groupedLogResources)
89         .reduce((grouped, key) => ({
90             ...grouped,
91             [key]: logsToLines(groupedLogResources[key])
92         }), {});
93     const filters = [SUMMARIZED_FILTER_TYPE, ...Object.keys(groupedLogs)];
94     const logs = { [SUMMARIZED_FILTER_TYPE]: allLogs, ...groupedLogs };
95     return { filters, logs };
96 };
97
98 const logsToLines = (logs: LogResource[]) =>
99     logs.map(({ properties }) => properties.text);
100
101 const MAX_AMOUNT_OF_LOGS = 10000;
102
103 const SUMMARIZED_FILTER_TYPE = 'Summarized';
104
105 const PROCESS_PANEL_LOG_EVENT_TYPES = [
106     LogEventType.ARV_MOUNT,
107     LogEventType.CRUNCH_RUN,
108     LogEventType.CRUNCHSTAT,
109     LogEventType.DISPATCH,
110     LogEventType.HOSTSTAT,
111     LogEventType.NODE_INFO,
112     LogEventType.STDERR,
113     LogEventType.STDOUT,
114 ];