Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/packages/console-shared/src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const COLUMN_MANAGEMENT_LOCAL_STORAGE_KEY = `${STORAGE_PREFIX}/table-colu
export const LOG_WRAP_LINES_USERSETTINGS_KEY = `${USERSETTINGS_PREFIX}.log.wrapLines`;
export const SHOW_YAML_EDITOR_TOOLTIPS_USER_SETTING_KEY = `${USERSETTINGS_PREFIX}.showYAMLEditorTooltips`;
export const SHOW_YAML_EDITOR_TOOLTIPS_LOCAL_STORAGE_KEY = `${STORAGE_PREFIX}/showYAMLEditorTooltips`;
export const SHOW_FULL_LOG_USERSETTINGS_KEY = `${USERSETTINGS_PREFIX}.show.full.log`;
// Bootstrap user for OpenShift 4.0 clusters (kube:admin)
export const KUBE_ADMIN_USERNAMES = ['kube:admin'];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { checkErrors } from '../../support';
import { detailsPage } from '../../views/details-page';
import { listPage, listPage } from '../../views/list-page';

describe('Pod log viewer tab', () => {
before(() => {
cy.login();
});

afterEach(() => {
checkErrors();
});

it('Open logs from pod details page tab and verify the log buffer sizes', () => {
cy.visit(
`/k8s/ns/openshift-kube-apiserver/core~v1~Pod?name=kube-apiserver-ip-&rowFilter-pod-status=Running&orderBy=desc&sortBy=Owner`,
);
listPage.rows.clickFirstLinkInFirstRow();
detailsPage.isLoaded();
detailsPage.selectTab('Logs');
detailsPage.isLoaded();
// Verify the default log buffer size
cy.byTestID('no-log-lines').contains('1000 lines');
// Verify the log exceeds the default log buffer size
cy.byTestID('show-full-log').check();
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000);
cy.byTestID('no-log-lines').should('not.contain', '1000 lines');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export const getDownloadAllLogsCallback = (
);
}
}
const buffer = new LineBuffer();
const buffer = new LineBuffer(null);
buffer.ingest(allLogs);
const blob = buffer.getBlob({
type: 'text/plain;charset=utf-8',
Expand Down
7 changes: 6 additions & 1 deletion frontend/public/components/utils/line-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ export class LineBuffer {
private _buffer: string[];
private _tail: string;
private _hasTruncated: boolean;
private _maxSize?: number;

constructor() {
constructor(maxSize) {
this._buffer = [];
this._tail = '';
this._hasTruncated = false;
this._maxSize = maxSize;
}

ingest(text): number {
Expand All @@ -22,6 +24,9 @@ export class LineBuffer {
this._hasTruncated = true;
}
if (/\n$/.test(line)) {
if (this._buffer.length === this._maxSize) {
this._buffer.shift();
}
this._buffer.push(_.truncate(next, { length: TRUNCATE_LENGTH }).trimEnd());
lineCount++;
this._tail = '';
Expand Down
77 changes: 63 additions & 14 deletions frontend/public/components/utils/resource-log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,32 @@ import * as React from 'react';
// @ts-ignore
import { useSelector } from 'react-redux';
import { Base64 } from 'js-base64';
import * as _ from 'lodash-es';
import { Trans, useTranslation } from 'react-i18next';
import { Alert, AlertActionLink, Button, Checkbox, Divider, Tooltip } from '@patternfly/react-core';
import {
Select as SelectDeprecated,
SelectOption as SelectOptionDeprecated,
SelectVariant as SelectVariantDeprecated,
} from '@patternfly/react-core/deprecated';
import { LogViewer, LogViewerSearch } from '@patternfly/react-log-viewer';

import * as _ from 'lodash-es';
import { Trans, useTranslation } from 'react-i18next';
import { CompressIcon } from '@patternfly/react-icons/dist/esm/icons/compress-icon';
import { ExpandIcon } from '@patternfly/react-icons/dist/esm/icons/expand-icon';
import { DownloadIcon } from '@patternfly/react-icons/dist/esm/icons/download-icon';
import { OutlinedWindowRestoreIcon } from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
import { OutlinedPlayCircleIcon } from '@patternfly/react-icons/dist/esm/icons/outlined-play-circle-icon';
import {
CompressIcon,
ExpandIcon,
DownloadIcon,
OutlinedWindowRestoreIcon,
OutlinedPlayCircleIcon,
} from '@patternfly/react-icons';
import * as classNames from 'classnames';
import { FLAGS, LOG_WRAP_LINES_USERSETTINGS_KEY } from '@console/shared/src/constants';
import {
FLAGS,
LOG_WRAP_LINES_USERSETTINGS_KEY,
SHOW_FULL_LOG_USERSETTINGS_KEY,
} from '@console/shared/src/constants';
import { useUserSettings } from '@console/shared';
import { LoadingInline, TogglePlay, ExternalLink } from './';
import { modelFor, resourceURL } from '../../module/k8s';
import { WSFactory } from '../../module/ws-factory';
import { LineBuffer } from './line-buffer';
import * as screenfull from 'screenfull';
import { RootState } from '@console/internal/redux';
import { k8sGet, k8sList, K8sResourceKind, PodKind } from '@console/internal/module/k8s';
Expand All @@ -36,6 +40,7 @@ import { Link } from 'react-router-dom';
import { resourcePath } from './resource-link';
import { isWindowsPod } from '../../module/k8s/pods';
import { getImpersonate } from '@console/dynamic-plugin-sdk';
import useToggleLineBuffer from './useToggleLineBuffer';

export const STREAM_EOF = 'eof';
export const STREAM_LOADING = 'loading';
Expand All @@ -50,6 +55,8 @@ export const LOG_SOURCE_WAITING = 'waiting';
const LOG_TYPE_CURRENT = 'current';
const LOG_TYPE_PREVIOUS = 'previous';

const DEFAULT_BUFFER_SIZE = 1000;

// Messages to display for corresponding log status
const streamStatusMessages = {
// t('public~Log stream ended.')
Expand Down Expand Up @@ -161,6 +168,8 @@ export const LogControls: React.FC<LogControlsProps> = ({
hasPreviousLog,
logType,
showLogTypeSelect,
isShowFullLog,
toggleShowFullLog,
}) => {
const { t } = useTranslation();
const [isLogTypeOpen, setLogTypeOpen] = React.useState(false);
Expand Down Expand Up @@ -234,7 +243,9 @@ export const LogControls: React.FC<LogControlsProps> = ({
</Tooltip>
);
};

const label = t('public~Debug container');

return (
<div className="co-toolbar">
<div className="co-toolbar__group co-toolbar__group--left">
Expand Down Expand Up @@ -314,6 +325,29 @@ export const LogControls: React.FC<LogControlsProps> = ({
</React.Fragment>
);
})}
<div>
<Tooltip
content={t(
'public~Select to view the entire log. Default view is the last 1,000 lines.',
)}
>
<Checkbox
label={t('public~Show full log')}
id="showFullLog"
data-test="show-full-log"
isChecked={isShowFullLog}
data-checked-state={isShowFullLog}
onChange={(_event, checked: boolean) => {
toggleShowFullLog(checked);
}}
/>
</Tooltip>
</div>
<Divider
orientation={{
default: 'vertical',
}}
/>
<Checkbox
label={t('public~Wrap lines')}
id="wrapLogLines"
Expand Down Expand Up @@ -371,13 +405,20 @@ export const LogControls: React.FC<LogControlsProps> = ({

// Resource agnostic log component
export const ResourceLog: React.FC<ResourceLogProps> = ({
bufferSize = DEFAULT_BUFFER_SIZE,
containerName,
dropdown,
resource,
resourceStatus,
}) => {
const { t } = useTranslation();
const buffer = React.useRef(new LineBuffer()); // TODO Make this a hook
const [showFullLog, setShowFullLog] = useUserSettings<boolean>(
SHOW_FULL_LOG_USERSETTINGS_KEY,
false,
true,
);
const [showFullLogCheckbox, setShowFullLogCheckbox] = React.useState(showFullLog);
const buffer = useToggleLineBuffer(showFullLogCheckbox ? null : bufferSize);
const ws = React.useRef<any>(); // TODO Make this a hook
const resourceLogRef = React.useRef();
const logViewerRef = React.useRef(null);
Expand Down Expand Up @@ -415,14 +456,17 @@ export const ResourceLog: React.FC<ResourceLogProps> = ({

const [wrapLinesCheckbox, setWrapLinesCheckbox] = React.useState(wrapLines || hasWrapAnnotation);
const firstRender = React.useRef(true);
const handleShowFullLogCheckbox = () => setShowFullLogCheckbox(!showFullLogCheckbox);

React.useEffect(() => {
if (firstRender.current) {
firstRender.current = false;
return;
}

setWrapLines(wrapLinesCheckbox);
}, [wrapLinesCheckbox, setWrapLines]);
setShowFullLog(showFullLogCheckbox);
}, [wrapLinesCheckbox, showFullLogCheckbox, setWrapLines, setShowFullLog]);

const timeoutIdRef = React.useRef(null);
const countRef = React.useRef(0);
Expand Down Expand Up @@ -532,7 +576,7 @@ export const ResourceLog: React.FC<ResourceLogProps> = ({
startWebSocket();
}
return () => ws.current?.destroy();
}, [error, resourceStatus, stale, startWebSocket]);
}, [error, resourceStatus, stale, startWebSocket, showFullLogCheckbox]);

// Toggle currently displayed log content to/from fullscreen
const toggleFullscreen = () => {
Expand Down Expand Up @@ -613,10 +657,12 @@ export const ResourceLog: React.FC<ResourceLogProps> = ({
namespaceUID={namespaceUID}
toggleWrapLines={setWrapLinesCheckbox}
isWrapLines={wrapLinesCheckbox}
isShowFullLog={showFullLogCheckbox}
hasPreviousLog={hasPreviousLogs}
changeLogType={setLogType}
logType={logType}
showLogTypeSelect={resource.kind === 'Pod'}
toggleShowFullLog={handleShowFullLogCheckbox}
/>
);

Expand Down Expand Up @@ -676,7 +722,7 @@ export const ResourceLog: React.FC<ResourceLogProps> = ({
<div className={classNames('resource-log__log-viewer-wrapper')}>
<LogViewer
header={
<div className="log-window__header">
<div className="log-window__header" data-test="no-log-lines">
<HeaderBanner lines={lines} />
</div>
}
Expand Down Expand Up @@ -721,13 +767,16 @@ type LogControlsProps = {
hasPreviousLog?: boolean;
logType: LogTypeStatus;
showLogTypeSelect: boolean;
toggleShowFullLog: (showFullLogCheckbox: boolean) => void;
isShowFullLog: boolean;
};

type ResourceLogProps = {
containerName?: string;
dropdown?: React.ReactNode;
resource: any;
resourceStatus: string;
bufferSize?: number;
};

type LogTypeStatus = typeof LOG_TYPE_CURRENT | typeof LOG_TYPE_PREVIOUS;
Expand Down
13 changes: 13 additions & 0 deletions frontend/public/components/utils/useToggleLineBuffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';
import { LineBuffer } from './line-buffer';

const useToggleLineBuffer = (bufferSize: number | null) => {
const buffer = React.useRef(null);
React.useEffect(() => {
buffer.current = new LineBuffer(bufferSize);
}, [bufferSize]);

return buffer;
};

export default useToggleLineBuffer;
2 changes: 2 additions & 0 deletions frontend/public/locales/en/public.json
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,8 @@
"Previous log": "Previous log",
"Only the current log is available for this container.": "Only the current log is available for this container.",
"Debug in terminal is not currently available for windows containers.": "Debug in terminal is not currently available for windows containers.",
"Select to view the entire log. Default view is the last 1,000 lines.": "Select to view the entire log. Default view is the last 1,000 lines.",
"Show full log": "Show full log",
"Wrap lines": "Wrap lines",
"Raw": "Raw",
"An error occurred while retrieving the requested logs.": "An error occurred while retrieving the requested logs.",
Expand Down