Skip to content

Commit

Permalink
Add a log viewer
Browse files Browse the repository at this point in the history
Add a log viewer that opens in a modal window when the
"Show logs" button in the upper right corner of the screen
is clicked.

The log viewer is currently hardcoded to follow /tmp/anaconda.log.
  • Loading branch information
M4rtinK committed Jun 9, 2022
1 parent 030b7e1 commit cbbdd7c
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 6 deletions.
17 changes: 15 additions & 2 deletions ui/webui/src/components/AnacondaHeader.jsx
Expand Up @@ -19,7 +19,8 @@ import cockpit from "cockpit";
import React from "react";

import {
Flex,
Button,
Flex, FlexItem,
Label,
PageSection, PageSectionVariants,
Popover,
Expand All @@ -31,7 +32,7 @@ const _ = cockpit.gettext;

const prerelease = _("Pre-release");

export const AnacondaHeader = ({ beta, title }) => {
export const AnacondaHeader = ({ beta, title, setShowLogViewer }) => {
const betanag = beta
? (
<Popover
Expand Down Expand Up @@ -63,6 +64,18 @@ export const AnacondaHeader = ({ beta, title }) => {
<Text component="h1">{title}</Text>
</TextContent>
{betanag}
<FlexItem align={{ default: "alignRight" }}>
<Button
aria-label={_("Show logs")}
id="global-show-logs-btn"
variant="tertiary"
onClick={() => {
setShowLogViewer(true);
}}
>
{_("Show logs")}
</Button>
</FlexItem>
</Flex>
</PageSection>
);
Expand Down
11 changes: 9 additions & 2 deletions ui/webui/src/components/AnacondaWizard.jsx
Expand Up @@ -33,12 +33,13 @@ import { InstallationDestination, applyDefaultStorage } from "./storage/Installa
import { InstallationLanguage } from "./localization/InstallationLanguage.jsx";
import { InstallationProgress } from "./installation/InstallationProgress.jsx";
import { ReviewConfiguration, ReviewConfigurationConfirmModal } from "./review/ReviewConfiguration.jsx";
import { LogViewerModal } from "./LogViewer.jsx";
import { exitGui } from "../helpers/exit.js";
import { usePageLocation } from "hooks";

const _ = cockpit.gettext;

export const AnacondaWizard = ({ onAddErrorNotification, title }) => {
export const AnacondaWizard = ({ onAddErrorNotification, title, showLogViewer, setShowLogViewer }) => {
const [isFormValid, setIsFormValid] = useState(true);
const [stepNotification, setStepNotification] = useState();
const [isInProgress, setIsInProgress] = useState(false);
Expand Down Expand Up @@ -103,6 +104,8 @@ export const AnacondaWizard = ({ onAddErrorNotification, title }) => {
setStepNotification={setStepNotification}
isInProgress={isInProgress}
setIsInProgress={setIsInProgress}
showLogViewer={showLogViewer}
setShowLogViewer={setShowLogViewer}
/>}
hideClose
mainAriaLabel={`${title} content`}
Expand All @@ -116,7 +119,7 @@ export const AnacondaWizard = ({ onAddErrorNotification, title }) => {
);
};

const Footer = ({ isFormValid, setIsFormValid, setStepNotification, isInProgress, setIsInProgress }) => {
const Footer = ({ isFormValid, setIsFormValid, setStepNotification, isInProgress, setIsInProgress, showLogViewer, setShowLogViewer }) => {
const [nextWaitsConfirmation, setNextWaitsConfirmation] = useState(false);
const [quitWaitsConfirmation, setQuitWaitsConfirmation] = useState(false);

Expand Down Expand Up @@ -179,6 +182,10 @@ const Footer = ({ isFormValid, setIsFormValid, setStepNotification, isInProgress
exitGui={exitGui}
setQuitWaitsConfirmation={setQuitWaitsConfirmation}
/>}
{showLogViewer &&
<LogViewerModal
setShowLogViewer={setShowLogViewer}
/>}
<ActionList>
<Button
id="installation-next-btn"
Expand Down
102 changes: 102 additions & 0 deletions ui/webui/src/components/LogViewer.jsx
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2022 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";

import React, { useEffect, useState } from "react";

import { LogViewer, LogViewerSearch } from "@patternfly/react-log-viewer";

import { watchLogFile } from "../helpers/logs.js";

import {
Button,
Modal, ModalVariant,
Toolbar, ToolbarContent, ToolbarItem,
Text, TextContent, TextVariants,
Flex,
} from "@patternfly/react-core";

const _ = cockpit.gettext;

export const AnacondaLogViewer = () => {
const [logData, setLogData] = useState("");

useEffect(() => {
const appendLogData = (newLogData, dataTag, error) => {
setLogData(logData + newLogData);
if (error) {
console.log("Log file read failed.");
console.log(error);
}
};
watchLogFile("/tmp/anaconda.log", appendLogData);
// TODO: find another way to run this only once without
// the need to disable eslint warnings
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<LogViewer
// TODO: find why line numbers are corrupted when displayed
hasLineNumbers={false}
isTextWrapped={false}
height={600}
data={logData}
toolbar={
<Toolbar>
<ToolbarContent>
<ToolbarItem>
<LogViewerSearch placeholder="Search value" />
</ToolbarItem>
</ToolbarContent>
</Toolbar>
}
/>
);
};

export const LogViewerModal = ({ setShowLogViewer }) => {
return (
<Modal
id="log-viewer-modal"
actions={[
<Button
id="log-viewer-exit-btn"
key="cancel"
onClick={() => {
setShowLogViewer(false);
}}
variant="secondary">
{_("Close")}
</Button>
]}
isOpen
onClose={() => setShowLogViewer(false)}
title={_("Installer logs")}
variant={ModalVariant.medium}
>
<Flex direction={{ default: "column" }}>
<TextContent>
<Text component={TextVariants.p}>
{_("Installer version") + " 37.10"}
</Text>
</TextContent>
<AnacondaLogViewer />
</Flex>
</Modal>
);
};
10 changes: 8 additions & 2 deletions ui/webui/src/components/app.jsx
Expand Up @@ -42,6 +42,7 @@ export const Application = () => {
const [beta, setBeta] = useState();
const [conf, setConf] = useState();
const [notifications, setNotifications] = useState({});
const [showLogViewer, setShowLogViewer] = useState(false);

useEffect(() => {
cockpit.file("/run/anaconda/bus.address").watch(address => {
Expand Down Expand Up @@ -90,7 +91,7 @@ export const Application = () => {
<Page
data-debug={conf.Anaconda.debug}
additionalGroupedContent={
<AnacondaHeader beta={beta} title={title} />
<AnacondaHeader beta={beta} title={title} setShowLogViewer={setShowLogViewer} />
}
groupProps={{
sticky: "top"
Expand Down Expand Up @@ -120,7 +121,12 @@ export const Application = () => {
})}
</AlertGroup>}
<AddressContext.Provider value={address}>
<AnacondaWizard onAddErrorNotification={onAddErrorNotification} title={title} />
<AnacondaWizard
onAddErrorNotification={onAddErrorNotification}
title={title}
showLogViewer={showLogViewer}
setShowLogViewer={setShowLogViewer}
/>
</AddressContext.Provider>
</Page>
);
Expand Down
22 changes: 22 additions & 0 deletions ui/webui/src/helpers/logs.js
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";

export const watchLogFile = (logFilePath, callback) => {
const file = cockpit.file(logFilePath);
file.watch(callback);
};

0 comments on commit cbbdd7c

Please sign in to comment.