Logs Re-Design (#1656)
This commit is contained in:
53
portal-ui/src/icons/BoxArrowDown.tsx
Normal file
53
portal-ui/src/icons/BoxArrowDown.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const BoxArrowDown = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 26 26"
|
||||
{...props}
|
||||
>
|
||||
<g id="Group_2476" data-name="Group 2476" transform="translate(-1898 -343)">
|
||||
<rect
|
||||
id="Rectangle_1114"
|
||||
data-name="Rectangle 1114"
|
||||
width="26"
|
||||
height="26"
|
||||
transform="translate(1898 343)"
|
||||
fill="#fbfafa"
|
||||
/>
|
||||
<g
|
||||
id="noun_chevron_2320228"
|
||||
transform="translate(1915.2 353.499) rotate(90)"
|
||||
>
|
||||
<path
|
||||
id="Path_6842"
|
||||
data-name="Path 6842"
|
||||
d="M.47,8a.464.464,0,0,1-.329-.141.468.468,0,0,1,0-.67L3.325,4.006.141.811a.468.468,0,0,1,0-.67.468.468,0,0,1,.67,0L4.335,3.665a.464.464,0,0,1,.141.329.427.427,0,0,1-.141.329L.811,7.847A.476.476,0,0,1,.47,8Z"
|
||||
transform="translate(0 0)"
|
||||
fill="#2781b0"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default BoxArrowDown;
|
||||
57
portal-ui/src/icons/BoxArrowUp.tsx
Normal file
57
portal-ui/src/icons/BoxArrowUp.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const BoxArrowUp = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 26 26"
|
||||
{...props}
|
||||
>
|
||||
<g
|
||||
id="Group_2001"
|
||||
data-name="Group 2001"
|
||||
transform="translate(1924 369) rotate(180)"
|
||||
>
|
||||
<rect
|
||||
id="Rectangle_1114"
|
||||
data-name="Rectangle 1114"
|
||||
width="26"
|
||||
height="26"
|
||||
transform="translate(1898 343)"
|
||||
fill="#e5e5e5"
|
||||
/>
|
||||
<g
|
||||
id="noun_chevron_2320228"
|
||||
transform="translate(1915.2 353.499) rotate(90)"
|
||||
>
|
||||
<path
|
||||
id="Path_6842"
|
||||
data-name="Path 6842"
|
||||
d="M.47,8a.464.464,0,0,1-.329-.141.468.468,0,0,1,0-.67L3.325,4.006.141.811a.468.468,0,0,1,0-.67.468.468,0,0,1,.67,0L4.335,3.665a.464.464,0,0,1,.141.329.427.427,0,0,1-.141.329L.811,7.847A.476.476,0,0,1,.47,8Z"
|
||||
transform="translate(0 0)"
|
||||
fill="#5e5e5e"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default BoxArrowUp;
|
||||
59
portal-ui/src/icons/WarnFilledIcon.tsx
Normal file
59
portal-ui/src/icons/WarnFilledIcon.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const WarnFilledIcon = (props: SVGProps<SVGSVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
id="WarnFilledIcon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 12 12"
|
||||
{...props}
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect
|
||||
id="Rectangle_987"
|
||||
data-name="Rectangle 987"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="warning-icon-full" transform="translate(-0.002 -0.003)">
|
||||
<g
|
||||
id="Group_2356"
|
||||
data-name="Group 2356"
|
||||
transform="translate(0.002 0.003)"
|
||||
clip-path="url(#clip-path)"
|
||||
>
|
||||
<path
|
||||
id="Path_7081"
|
||||
data-name="Path 7081"
|
||||
d="M6,0H6a6,6,0,1,0,6,6A6,6,0,0,0,6,0m.964,1.947L6.751,7.434H5.318L5.1,1.947ZM6.04,10.454a1.134,1.134,0,1,1,0-2.269,1.134,1.134,0,0,1,0,2.269"
|
||||
transform="translate(-0.002 -0.003)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarnFilledIcon;
|
||||
@@ -24,7 +24,6 @@ import moment from "moment/moment";
|
||||
import { AppState } from "../../../../store";
|
||||
import { logMessageReceived, logResetMessages } from "../actions";
|
||||
import { LogMessage } from "../types";
|
||||
import { timeFromDate } from "../../../../common/utils";
|
||||
import { wsProtocol } from "../../../../utils/wsUtils";
|
||||
import {
|
||||
actionsTray,
|
||||
@@ -35,6 +34,11 @@ import {
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import SearchBox from "../../Common/SearchBox";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableContainer from "@mui/material/TableContainer";
|
||||
import LogLine from "./LogLine";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -44,7 +48,6 @@ const styles = (theme: Theme) =>
|
||||
height: "calc(100vh - 280px)",
|
||||
overflow: "auto",
|
||||
fontSize: 13,
|
||||
padding: "15px 15px 0",
|
||||
border: "1px solid #EAEDEE",
|
||||
borderRadius: 4,
|
||||
},
|
||||
@@ -85,7 +88,7 @@ const ErrorLogs = ({
|
||||
logResetMessages,
|
||||
messages,
|
||||
}: ILogs) => {
|
||||
const [highlight, setHighlight] = useState("");
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
logResetMessages();
|
||||
@@ -128,218 +131,57 @@ const ErrorLogs = ({
|
||||
}
|
||||
}, [logMessageReceived, logResetMessages]);
|
||||
|
||||
const renderError = (logElement: LogMessage) => {
|
||||
let errorElems = [];
|
||||
if (logElement.error !== null && logElement.error !== undefined) {
|
||||
if (logElement.api && logElement.api.name) {
|
||||
const errorText = `API: ${logElement.api.name}`;
|
||||
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`api-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<br />
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.time) {
|
||||
const errorText = `Time: ${timeFromDate(logElement.time)}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`time-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.deploymentid) {
|
||||
const errorText = `DeploymentID: ${logElement.deploymentid}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`deploytmentid-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.requestID) {
|
||||
const errorText = `RequestID: ${logElement.requestID}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`requestid-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.remotehost) {
|
||||
const errorText = `RemoteHost: ${logElement.remotehost}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`remotehost-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.host) {
|
||||
const errorText = `Host: ${logElement.host}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`host-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.userAgent) {
|
||||
const errorText = `UserAgent: ${logElement.userAgent}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`useragent-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.error.message) {
|
||||
const errorText = `Error: ${logElement.error.message}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`message-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.error.source) {
|
||||
// for all sources add padding
|
||||
for (let s in logElement.error.source) {
|
||||
const errorText = logElement.error.source[s];
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<div
|
||||
key={`source-${logElement.key}-${s}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror_tab}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const filtLow = filter.toLowerCase();
|
||||
let filteredMessages = messages.filter((m) => {
|
||||
if (filter !== "") {
|
||||
if (m.ConsoleMsg.toLowerCase().indexOf(filtLow) >= 0) {
|
||||
return true;
|
||||
} else if (
|
||||
m.error &&
|
||||
m.error.source &&
|
||||
m.error.source.filter((x) => {
|
||||
return x.toLowerCase().indexOf(filtLow) >= 0;
|
||||
}).length > 0
|
||||
) {
|
||||
return true;
|
||||
} else if (
|
||||
m.error &&
|
||||
m.error.message.toLowerCase().indexOf(filtLow) >= 0
|
||||
) {
|
||||
return true;
|
||||
} else if (m.api && m.api.name.toLowerCase().indexOf(filtLow) >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return errorElems;
|
||||
};
|
||||
|
||||
const renderLog = (logElement: LogMessage) => {
|
||||
let logMessage = logElement.ConsoleMsg;
|
||||
// remove any non ascii characters, exclude any control codes
|
||||
logMessage = logMessage.replace(/([^\x20-\x7F])/g, "");
|
||||
|
||||
// regex for terminal colors like e.g. `[31;4m `
|
||||
const tColorRegex = /((\[[0-9;]+m))/g;
|
||||
|
||||
// get substring if there was a match for to split what
|
||||
// is going to be colored and what not, here we add color
|
||||
// only to the first match.
|
||||
let substr = logMessage.replace(tColorRegex, "");
|
||||
|
||||
// in case highlight is set, we select the line that contains the requested string
|
||||
let highlightedLine =
|
||||
highlight !== ""
|
||||
? logMessage.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
|
||||
// if starts with multiple spaces add padding
|
||||
if (substr.startsWith(" ")) {
|
||||
return (
|
||||
<div
|
||||
key={logElement.key}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.tab}>{substr}</span>
|
||||
</div>
|
||||
);
|
||||
} else if (logElement.error !== null && logElement.error !== undefined) {
|
||||
// list error message and all sources and error elems
|
||||
return renderError(logElement);
|
||||
} else {
|
||||
// for all remaining set default class
|
||||
return (
|
||||
<div
|
||||
key={logElement.key}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.ansidefault}>{substr}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderLines = messages.map((m) => {
|
||||
return renderLog(m);
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label="Logs" />
|
||||
<PageLayout>
|
||||
<Grid xs={12}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<SearchBox
|
||||
placeholder="Highlight Line"
|
||||
onChange={setHighlight}
|
||||
value={highlight}
|
||||
placeholder="Filter"
|
||||
onChange={(e) => {
|
||||
setFilter(e);
|
||||
}}
|
||||
value={filter}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div id="logs-container" className={classes.logList}>
|
||||
{renderLines}
|
||||
<TableContainer component={Paper}>
|
||||
<Table aria-label="collapsible table">
|
||||
<TableBody>
|
||||
{filteredMessages.map((m) => {
|
||||
return <LogLine log={m} />;
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
232
portal-ui/src/screens/Console/Logs/ErrorLogs/LogLine.tsx
Normal file
232
portal-ui/src/screens/Console/Logs/ErrorLogs/LogLine.tsx
Normal file
@@ -0,0 +1,232 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { LogMessage } from "../types";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Moment from "react-moment";
|
||||
import BoxArrowUp from "../../../../icons/BoxArrowUp";
|
||||
import BoxArrowDown from "../../../../icons/BoxArrowDown";
|
||||
import WarnFilledIcon from "../../../../icons/WarnFilledIcon";
|
||||
|
||||
const messageForConsoleMsg = (log: LogMessage) => {
|
||||
// regex for terminal colors like e.g. `[31;4m `
|
||||
const tColorRegex = /((\[[0-9;]+m))/g;
|
||||
|
||||
let fullMessage = log.ConsoleMsg;
|
||||
// remove the 0x1B character
|
||||
/* eslint-disable no-control-regex */
|
||||
fullMessage = fullMessage.replace(/\x1B/g, " ");
|
||||
/* eslint-enable no-control-regex */
|
||||
// get substring if there was a match for to split what
|
||||
// is going to be colored and what not, here we add color
|
||||
// only to the first match.
|
||||
fullMessage = fullMessage.replace(tColorRegex, "");
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "table",
|
||||
tableLayout: "fixed",
|
||||
width: "100%",
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "table-cell",
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflowX: "auto",
|
||||
}}
|
||||
>
|
||||
<pre>{fullMessage}</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const messageForError = (log: LogMessage) => {
|
||||
const dataStyle = { color: "green" };
|
||||
return (
|
||||
<Fragment>
|
||||
<div>
|
||||
<b>API: </b>
|
||||
<span style={dataStyle}>{log.api.name}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>Time: </b>
|
||||
<span style={dataStyle}>{log.time.toString()}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>DeploymentID: </b>
|
||||
<span style={dataStyle}>{log.deploymentid}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>RequestID: </b>
|
||||
<span style={dataStyle}>{log.requestID}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>RemoteHost: </b>
|
||||
<span style={dataStyle}>{log.remotehost}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>UserAgent: </b>
|
||||
<span style={dataStyle}>{log.userAgent}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>Error: </b>
|
||||
<span style={dataStyle}>{log.error && log.error.message}</span>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<b>Backtrace: </b>
|
||||
</div>
|
||||
{log.error &&
|
||||
log.error.source.map((e, i) => {
|
||||
return (
|
||||
<div>
|
||||
<b>{i}: </b>
|
||||
<span style={dataStyle}>{e}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const LogLine = (props: { log: LogMessage }) => {
|
||||
const { log } = props;
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
let logMessage = "";
|
||||
if (log.ConsoleMsg !== "") {
|
||||
logMessage = log.ConsoleMsg;
|
||||
} else if (log.error !== null && log.error.message !== "") {
|
||||
logMessage = log.error.message;
|
||||
}
|
||||
// remove any non ascii characters, exclude any control codes
|
||||
let titleLogMessage = logMessage.replace(/━|┏|┓|┃|┗|┛/g, "");
|
||||
// remove any non ascii characters, exclude any control codes
|
||||
titleLogMessage = titleLogMessage.replace(/([^\x20-\x7F])/g, "");
|
||||
|
||||
// regex for terminal colors like e.g. `[31;4m `
|
||||
const tColorRegex = /((\[[0-9;]+m))/g;
|
||||
|
||||
let fullMessage = <Fragment />;
|
||||
if (log.ConsoleMsg !== "") {
|
||||
fullMessage = messageForConsoleMsg(log);
|
||||
} else if (log.error !== null && log.error.message !== "") {
|
||||
fullMessage = messageForError(log);
|
||||
}
|
||||
|
||||
titleLogMessage = titleLogMessage.replace(tColorRegex, "");
|
||||
|
||||
let dateStr = <Moment format="YYYY/MM/DD UTC HH:mm:ss">{log.time}</Moment>;
|
||||
if (log.time.getFullYear() === 1) {
|
||||
dateStr = <Fragment>n/a</Fragment>;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key={log.time.toString()}>
|
||||
<TableRow
|
||||
sx={{ "& > *": { borderBottom: "unset" }, cursor: "pointer" }}
|
||||
style={{ backgroundColor: "#FDFDFD" }}
|
||||
>
|
||||
<TableCell
|
||||
onClick={() => setOpen(!open)}
|
||||
style={{ width: 200, color: "#989898", fontSize: 12 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
"& .min-icon": { width: 12, marginRight: 1 },
|
||||
fontWeight: "bold",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
<WarnFilledIcon />
|
||||
{dateStr}
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell onClick={() => setOpen(!open)}>
|
||||
<div
|
||||
style={{
|
||||
display: "table",
|
||||
tableLayout: "fixed",
|
||||
width: "100%",
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "table-cell",
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{titleLogMessage}
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell onClick={() => setOpen(!open)} style={{ width: 40 }}>
|
||||
{open ? <BoxArrowUp /> : <BoxArrowDown />}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
style={{
|
||||
paddingBottom: 0,
|
||||
paddingTop: 0,
|
||||
width: 200,
|
||||
textTransform: "uppercase",
|
||||
verticalAlign: "top",
|
||||
textAlign: "right",
|
||||
color: "#8399AB",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<div style={{ marginTop: 10 }}>Log Details</div>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }}>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<Box sx={{ margin: 1 }}>
|
||||
<Typography
|
||||
style={{
|
||||
background: "#efefef",
|
||||
border: "1px solid #dedede",
|
||||
padding: 4,
|
||||
fontSize: 14,
|
||||
color: "#666666",
|
||||
}}
|
||||
>
|
||||
{fullMessage}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0, width: 40 }} />
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogLine;
|
||||
@@ -35,9 +35,29 @@ export function logReducer(
|
||||
): LogState {
|
||||
switch (action.type) {
|
||||
case LOG_MESSAGE_RECEIVED:
|
||||
// if it's a simple ConsoleMsg, append it to the current ConsoleMsg in the
|
||||
// state if any
|
||||
let msgs = [...state.messages];
|
||||
|
||||
if (
|
||||
msgs.length > 0 &&
|
||||
action.message.time.getFullYear() === 1 &&
|
||||
action.message.ConsoleMsg !== ""
|
||||
) {
|
||||
for (let m in msgs) {
|
||||
if (msgs[m].time.getFullYear() === 1) {
|
||||
msgs[
|
||||
m
|
||||
].ConsoleMsg = `${msgs[m].ConsoleMsg}\n${action.message.ConsoleMsg}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msgs.push(action.message);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, action.message],
|
||||
messages: msgs,
|
||||
};
|
||||
case LOG_RESET_MESSAGES:
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user