X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/abc9ef71a341f72c061c6fc8b0276fcf9937c411..6796b44d4934ddff098e1bfcf7b842ec11e4e210:/src/store/process-logs-panel/process-logs-panel-actions.ts diff --git a/src/store/process-logs-panel/process-logs-panel-actions.ts b/src/store/process-logs-panel/process-logs-panel-actions.ts index 9da7d1b9..87a2fa12 100644 --- a/src/store/process-logs-panel/process-logs-panel-actions.ts +++ b/src/store/process-logs-panel/process-logs-panel-actions.ts @@ -13,6 +13,7 @@ import { Process, getProcess } from 'store/processes/process'; import { navigateTo } from 'store/navigation/navigation-action'; import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; import { CollectionFile, CollectionFileType } from "models/collection-file"; +import { ContainerRequestResource } from "models/container-request"; const SNIPLINE = `================ ✀ ================ ✀ ========= Some log(s) were skipped ========= ✀ ================ ✀ ================`; const LOG_TIMESTAMP_PATTERN = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{9}Z/; @@ -44,7 +45,7 @@ export const initProcessLogsPanel = (processUuid: string) => const process = getProcess(processUuid)(getState().resources); if (process?.containerRequest?.uuid) { // Get log file size info - const logFiles = await loadContainerLogFileList(process.containerRequest.uuid, logService); + const logFiles = await loadContainerLogFileList(process.containerRequest, logService); // Populate lastbyte 0 for each file const filesWithProgress = logFiles.map((file) => ({file, lastByte: 0})); @@ -60,7 +61,10 @@ export const initProcessLogsPanel = (processUuid: string) => // On error, populate empty state to allow polling to start const initialState = createInitialLogPanelState([], []); dispatch(processLogsPanelActions.INIT_PROCESS_LOGS_PANEL(initialState)); - dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not load process logs', hideDuration: 2000, kind: SnackbarKind.ERROR })); + // Only show toast on errors other than 404 since 404 is expected when logs do not exist yet + if (e.status !== 404) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not load process logs', hideDuration: 2000, kind: SnackbarKind.ERROR })); + } } }; @@ -73,7 +77,7 @@ export const pollProcessLogs = (processUuid: string) => // Check if container request is present and initial logs state loaded if (process?.containerRequest?.uuid && Object.keys(currentState.logs).length > 0) { - const logFiles = await loadContainerLogFileList(process.containerRequest.uuid, logService); + const logFiles = await loadContainerLogFileList(process.containerRequest, logService); // Determine byte to fetch from while filtering unchanged files const filesToUpdateWithProgress = logFiles.reduce((acc, updatedFile) => { @@ -102,13 +106,13 @@ export const pollProcessLogs = (processUuid: string) => return Promise.resolve(); } catch (e) { // Remove log when polling error is handled in some way instead of being ignored - console.log("Polling process logs failed"); + console.error("Error occurred in pollProcessLogs:", e); return Promise.reject(); } }; -const loadContainerLogFileList = async (containerUuid: string, logService: LogService) => { - const logCollectionContents = await logService.listLogFiles(containerUuid); +const loadContainerLogFileList = async (containerRequest: ContainerRequestResource, logService: LogService) => { + const logCollectionContents = await logService.listLogFiles(containerRequest); // Filter only root directory files matching log event types which have bytes return logCollectionContents.filter((file): file is CollectionFile => ( @@ -134,25 +138,28 @@ const loadContainerLogFileContents = async (logFilesWithProgress: FileWithProgre const chunkSize = Math.floor(maxLogFetchSize / 2); const firstChunkEnd = lastByte+chunkSize-1; return Promise.all([ - logService.getLogFileContents(process.containerRequest.uuid, file, lastByte, firstChunkEnd), - logService.getLogFileContents(process.containerRequest.uuid, file, file.size-chunkSize, file.size-1) + logService.getLogFileContents(process.containerRequest, file, lastByte, firstChunkEnd), + logService.getLogFileContents(process.containerRequest, file, file.size-chunkSize, file.size-1) ] as Promise<(LogFragment)>[]); } else { - return Promise.all([logService.getLogFileContents(process.containerRequest.uuid, file, lastByte, file.size-1)]); + return Promise.all([logService.getLogFileContents(process.containerRequest, file, lastByte, file.size-1)]); } })).then((res) => { - if (res.length && res.every(promise => (promise.status === 'rejected'))) { + if (res.length && res.every(promiseResult => (promiseResult.status === 'rejected'))) { // Since allSettled does not pass promise rejection we throw an // error if every request failed - return Promise.reject("Failed to load logs"); + const error = res.find( + (promiseResult): promiseResult is PromiseRejectedResult => promiseResult.status === 'rejected' + )?.reason; + return Promise.reject(error); } - return res.filter((one): one is PromiseFulfilledResult => ( + return res.filter((promiseResult): promiseResult is PromiseFulfilledResult => ( // Filter out log files with rejected promises // (Promise.all rejects on any failure) - one.status === 'fulfilled' && + promiseResult.status === 'fulfilled' && // Filter out files where any fragment is empty // (prevent incorrect snipline generation or an un-resumable situation) - !!one.value.every(logFragment => logFragment.contents.length) + !!promiseResult.value.every(logFragment => logFragment.contents.length) )).map(one => one.value) })).map((logResponseSet)=> { // For any multi fragment response set, modify the last line of non-final chunks to include a line break and snip line