Use React hook to better deal with websockets (#3299)
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
"react-pdf": "7.7.1",
|
"react-pdf": "7.7.1",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^8.1.3",
|
||||||
"react-router-dom": "6.22.3",
|
"react-router-dom": "6.22.3",
|
||||||
|
"react-use-websocket": "^4.8.1",
|
||||||
"react-virtualized": "^9.22.5",
|
"react-virtualized": "^9.22.5",
|
||||||
"react-window": "^1.8.10",
|
"react-window": "^1.8.10",
|
||||||
"react-window-infinite-loader": "^1.0.9",
|
"react-window-infinite-loader": "^1.0.9",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Fragment, useEffect, useState } from "react";
|
import { Fragment, useEffect, useState } from "react";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
@@ -41,8 +41,7 @@ import { setHelpName } from "../../../systemSlice";
|
|||||||
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
|
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
|
||||||
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
|
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
|
||||||
import HelpMenu from "../HelpMenu";
|
import HelpMenu from "../HelpMenu";
|
||||||
|
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||||
var socket: any = null;
|
|
||||||
|
|
||||||
const Trace = () => {
|
const Trace = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -65,68 +64,82 @@ const Trace = () => {
|
|||||||
const [errors, setErrors] = useState<boolean>(false);
|
const [errors, setErrors] = useState<boolean>(false);
|
||||||
|
|
||||||
const [toggleFilter, setToggleFilter] = useState<boolean>(false);
|
const [toggleFilter, setToggleFilter] = useState<boolean>(false);
|
||||||
|
const [logActive, setLogActive] = useState(false);
|
||||||
|
const [wsUrl, setWsUrl] = useState<string>("");
|
||||||
|
|
||||||
const startTrace = () => {
|
useEffect(() => {
|
||||||
dispatch(traceResetMessages());
|
|
||||||
const url = new URL(window.location.toString());
|
const url = new URL(window.location.toString());
|
||||||
const isDev = process.env.NODE_ENV === "development";
|
|
||||||
const port = isDev ? "9090" : url.port;
|
|
||||||
|
|
||||||
let calls = `${s3 ? "s3," : ""}${internal ? "internal," : ""}${
|
|
||||||
storage ? "storage," : ""
|
|
||||||
}${os ? "os," : ""}`;
|
|
||||||
|
|
||||||
if (all) {
|
|
||||||
calls = "all";
|
|
||||||
}
|
|
||||||
// check if we are using base path, if not this always is `/`
|
|
||||||
const baseLocation = new URL(document.baseURI);
|
|
||||||
const baseUrl = baseLocation.pathname;
|
|
||||||
|
|
||||||
const wsProt = wsProtocol(url.protocol);
|
const wsProt = wsProtocol(url.protocol);
|
||||||
socket = new WebSocket(
|
const port = process.env.NODE_ENV === "development" ? "9090" : url.port;
|
||||||
`${wsProt}://${
|
const calls = all
|
||||||
url.hostname
|
? "all"
|
||||||
}:${port}${baseUrl}ws/trace?calls=${calls}&threshold=${threshold}&onlyErrors=${
|
: (() => {
|
||||||
errors ? "yes" : "no"
|
const c = [];
|
||||||
}&statusCode=${statusCode}&method=${method}&funcname=${func}&path=${path}`,
|
if (s3) c.push("s3");
|
||||||
|
if (internal) c.push("internal");
|
||||||
|
if (storage) c.push("storage");
|
||||||
|
if (os) c.push("os");
|
||||||
|
return c.join(",");
|
||||||
|
})();
|
||||||
|
|
||||||
|
// check if we are using base path, if not this always is `/`
|
||||||
|
const baseLocation = new URL(document.baseURI).pathname;
|
||||||
|
|
||||||
|
const wsUrl = new URL(
|
||||||
|
`${wsProt}://${url.hostname}:${port}${baseLocation}ws/trace`,
|
||||||
|
);
|
||||||
|
wsUrl.searchParams.append("calls", calls);
|
||||||
|
wsUrl.searchParams.append("threshold", threshold.toString());
|
||||||
|
wsUrl.searchParams.append("onlyErrors", errors ? "yes" : "no");
|
||||||
|
wsUrl.searchParams.append("statusCode", statusCode);
|
||||||
|
wsUrl.searchParams.append("method", method);
|
||||||
|
wsUrl.searchParams.append("funcname", func);
|
||||||
|
wsUrl.searchParams.append("path", path);
|
||||||
|
setWsUrl(wsUrl.href);
|
||||||
|
}, [
|
||||||
|
all,
|
||||||
|
s3,
|
||||||
|
internal,
|
||||||
|
storage,
|
||||||
|
os,
|
||||||
|
threshold,
|
||||||
|
errors,
|
||||||
|
statusCode,
|
||||||
|
method,
|
||||||
|
func,
|
||||||
|
path,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { sendMessage, lastJsonMessage, readyState } =
|
||||||
|
useWebSocket<TraceMessage>(
|
||||||
|
wsUrl,
|
||||||
|
{
|
||||||
|
heartbeat: {
|
||||||
|
message: "ok",
|
||||||
|
interval: 10 * 1000, // send ok every 10 seconds
|
||||||
|
timeout: 365 * 24 * 60 * 60 * 1000, // disconnect after 365 days (workaround, because heartbeat gets no response)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logActive,
|
||||||
);
|
);
|
||||||
|
|
||||||
let interval: any | null = null;
|
useEffect(() => {
|
||||||
if (socket !== null) {
|
if (readyState === ReadyState.CONNECTING) {
|
||||||
socket.onopen = () => {
|
dispatch(traceResetMessages());
|
||||||
console.log("WebSocket Client Connected");
|
} else if (readyState === ReadyState.OPEN) {
|
||||||
dispatch(setTraceStarted(true));
|
dispatch(setTraceStarted(true));
|
||||||
socket.send("ok");
|
} else if (readyState === ReadyState.CLOSED) {
|
||||||
interval = setInterval(() => {
|
dispatch(setTraceStarted(false));
|
||||||
socket.send("ok");
|
|
||||||
}, 10 * 1000);
|
|
||||||
};
|
|
||||||
socket.onmessage = (message: MessageEvent) => {
|
|
||||||
let m: TraceMessage = JSON.parse(message.data.toString());
|
|
||||||
|
|
||||||
m.ptime = DateTime.fromISO(m.time).toJSDate();
|
|
||||||
m.key = Math.random();
|
|
||||||
dispatch(traceMessageReceived(m));
|
|
||||||
};
|
|
||||||
socket.onclose = () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
console.log("connection closed by server");
|
|
||||||
dispatch(setTraceStarted(false));
|
|
||||||
};
|
|
||||||
return () => {
|
|
||||||
socket.close(1000);
|
|
||||||
clearInterval(interval);
|
|
||||||
console.log("closing websockets");
|
|
||||||
setTraceStarted(false);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
}, [readyState, dispatch, sendMessage]);
|
||||||
|
|
||||||
const stopTrace = () => {
|
useEffect(() => {
|
||||||
socket.close(1000);
|
if (lastJsonMessage) {
|
||||||
dispatch(setTraceStarted(false));
|
lastJsonMessage.ptime = DateTime.fromISO(lastJsonMessage.time).toJSDate();
|
||||||
};
|
lastJsonMessage.key = Math.random();
|
||||||
|
dispatch(traceMessageReceived(lastJsonMessage));
|
||||||
|
}
|
||||||
|
}, [lastJsonMessage, dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setHelpName("trace"));
|
dispatch(setHelpName("trace"));
|
||||||
@@ -187,9 +200,7 @@ const Trace = () => {
|
|||||||
id={"all_calls"}
|
id={"all_calls"}
|
||||||
name={"all_calls"}
|
name={"all_calls"}
|
||||||
label={"All"}
|
label={"All"}
|
||||||
onChange={() => {
|
onChange={() => setAll(!all)}
|
||||||
setAll(!all);
|
|
||||||
}}
|
|
||||||
value={"all"}
|
value={"all"}
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
@@ -198,9 +209,7 @@ const Trace = () => {
|
|||||||
id={"s3_calls"}
|
id={"s3_calls"}
|
||||||
name={"s3_calls"}
|
name={"s3_calls"}
|
||||||
label={"S3"}
|
label={"S3"}
|
||||||
onChange={() => {
|
onChange={() => setS3(!s3)}
|
||||||
setS3(!s3);
|
|
||||||
}}
|
|
||||||
value={"s3"}
|
value={"s3"}
|
||||||
disabled={all || traceStarted}
|
disabled={all || traceStarted}
|
||||||
/>
|
/>
|
||||||
@@ -209,9 +218,7 @@ const Trace = () => {
|
|||||||
id={"internal_calls"}
|
id={"internal_calls"}
|
||||||
name={"internal_calls"}
|
name={"internal_calls"}
|
||||||
label={"Internal"}
|
label={"Internal"}
|
||||||
onChange={() => {
|
onChange={() => setInternal(!internal)}
|
||||||
setInternal(!internal);
|
|
||||||
}}
|
|
||||||
value={"internal"}
|
value={"internal"}
|
||||||
disabled={all || traceStarted}
|
disabled={all || traceStarted}
|
||||||
/>
|
/>
|
||||||
@@ -220,9 +227,7 @@ const Trace = () => {
|
|||||||
id={"storage_calls"}
|
id={"storage_calls"}
|
||||||
name={"storage_calls"}
|
name={"storage_calls"}
|
||||||
label={"Storage"}
|
label={"Storage"}
|
||||||
onChange={() => {
|
onChange={() => setStorage(!storage)}
|
||||||
setStorage(!storage);
|
|
||||||
}}
|
|
||||||
value={"storage"}
|
value={"storage"}
|
||||||
disabled={all || traceStarted}
|
disabled={all || traceStarted}
|
||||||
/>
|
/>
|
||||||
@@ -231,9 +236,7 @@ const Trace = () => {
|
|||||||
id={"os_calls"}
|
id={"os_calls"}
|
||||||
name={"os_calls"}
|
name={"os_calls"}
|
||||||
label={"OS"}
|
label={"OS"}
|
||||||
onChange={() => {
|
onChange={() => setOS(!os)}
|
||||||
setOS(!os);
|
|
||||||
}}
|
|
||||||
value={"os"}
|
value={"os"}
|
||||||
disabled={all || traceStarted}
|
disabled={all || traceStarted}
|
||||||
/>
|
/>
|
||||||
@@ -249,9 +252,7 @@ const Trace = () => {
|
|||||||
<TooltipWrapper tooltip={"More filter options"}>
|
<TooltipWrapper tooltip={"More filter options"}>
|
||||||
<Button
|
<Button
|
||||||
id={"filter-toggle"}
|
id={"filter-toggle"}
|
||||||
onClick={() => {
|
onClick={() => setToggleFilter(!toggleFilter)}
|
||||||
setToggleFilter(!toggleFilter);
|
|
||||||
}}
|
|
||||||
label={"Filters"}
|
label={"Filters"}
|
||||||
icon={<FilterIcon />}
|
icon={<FilterIcon />}
|
||||||
variant={"regular"}
|
variant={"regular"}
|
||||||
@@ -269,7 +270,7 @@ const Trace = () => {
|
|||||||
label={"Start"}
|
label={"Start"}
|
||||||
data-test-id={"trace-start-button"}
|
data-test-id={"trace-start-button"}
|
||||||
variant="callAction"
|
variant="callAction"
|
||||||
onClick={startTrace}
|
onClick={() => setLogActive(true)}
|
||||||
style={{
|
style={{
|
||||||
width: "118px",
|
width: "118px",
|
||||||
}}
|
}}
|
||||||
@@ -281,7 +282,7 @@ const Trace = () => {
|
|||||||
label={"Stop Trace"}
|
label={"Stop Trace"}
|
||||||
data-test-id={"trace-stop-button"}
|
data-test-id={"trace-stop-button"}
|
||||||
variant="callAction"
|
variant="callAction"
|
||||||
onClick={stopTrace}
|
onClick={() => setLogActive(false)}
|
||||||
style={{
|
style={{
|
||||||
width: "118px",
|
width: "118px",
|
||||||
}}
|
}}
|
||||||
@@ -330,9 +331,7 @@ const Trace = () => {
|
|||||||
label="Status Code"
|
label="Status Code"
|
||||||
placeholder="e.g. 503"
|
placeholder="e.g. 503"
|
||||||
value={statusCode}
|
value={statusCode}
|
||||||
onChange={(e) => {
|
onChange={(e) => setStatusCode(e.target.value)}
|
||||||
setStatusCode(e.target.value);
|
|
||||||
}}
|
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -343,9 +342,7 @@ const Trace = () => {
|
|||||||
label="Function Name"
|
label="Function Name"
|
||||||
placeholder="e.g. FunctionName2055"
|
placeholder="e.g. FunctionName2055"
|
||||||
value={func}
|
value={func}
|
||||||
onChange={(e) => {
|
onChange={(e) => setFunc(e.target.value)}
|
||||||
setFunc(e.target.value);
|
|
||||||
}}
|
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -356,9 +353,7 @@ const Trace = () => {
|
|||||||
label="Method"
|
label="Method"
|
||||||
placeholder="e.g. Method 2056"
|
placeholder="e.g. Method 2056"
|
||||||
value={method}
|
value={method}
|
||||||
onChange={(e) => {
|
onChange={(e) => setMethod(e.target.value)}
|
||||||
setMethod(e.target.value);
|
|
||||||
}}
|
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -384,9 +379,7 @@ const Trace = () => {
|
|||||||
label="Path"
|
label="Path"
|
||||||
placeholder="e.g. my-bucket/my-prefix/*"
|
placeholder="e.g. my-bucket/my-prefix/*"
|
||||||
value={path}
|
value={path}
|
||||||
onChange={(e) => {
|
onChange={(e) => setPath(e.target.value)}
|
||||||
setPath(e.target.value);
|
|
||||||
}}
|
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -403,9 +396,7 @@ const Trace = () => {
|
|||||||
type="number"
|
type="number"
|
||||||
placeholder="e.g. website.io.3249.114.12"
|
placeholder="e.g. website.io.3249.114.12"
|
||||||
value={`${threshold}`}
|
value={`${threshold}`}
|
||||||
onChange={(e) => {
|
onChange={(e) => setThreshold(parseInt(e.target.value))}
|
||||||
setThreshold(parseInt(e.target.value));
|
|
||||||
}}
|
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -423,9 +414,7 @@ const Trace = () => {
|
|||||||
id={"only_errors"}
|
id={"only_errors"}
|
||||||
name={"only_errors"}
|
name={"only_errors"}
|
||||||
label={"Display only Errors"}
|
label={"Display only Errors"}
|
||||||
onChange={() => {
|
onChange={() => setErrors(!errors)}
|
||||||
setErrors(!errors);
|
|
||||||
}}
|
|
||||||
value={"only_errors"}
|
value={"only_errors"}
|
||||||
disabled={traceStarted}
|
disabled={traceStarted}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10615,6 +10615,11 @@ react-transition-group@^4.4.5:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
|
react-use-websocket@^4.8.1:
|
||||||
|
version "4.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-use-websocket/-/react-use-websocket-4.8.1.tgz#be06a0bc956c6d56391a29cbe2caa6ae8edb5615"
|
||||||
|
integrity sha512-FTXuG5O+LFozmu1BRfrzl7UIQngECvGJmL7BHsK4TYXuVt+mCizVA8lT0hGSIF0Z0TedF7bOo1nRzOUdginhDw==
|
||||||
|
|
||||||
react-virtual@^2.8.2:
|
react-virtual@^2.8.2:
|
||||||
version "2.10.4"
|
version "2.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.10.4.tgz#08712f0acd79d7d6f7c4726f05651a13b24d8704"
|
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.10.4.tgz#08712f0acd79d7d6f7c4726f05651a13b24d8704"
|
||||||
|
|||||||
Reference in New Issue
Block a user