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/;
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}));
// 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 }));
+ }
}
};
// 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) => {
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 => (
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<LogFragment[]> => (
+ return res.filter((promiseResult): promiseResult is PromiseFulfilledResult<LogFragment[]> => (
// 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