Skip to content

Commit

Permalink
[Dashboard] Migrate Logs page to use state api. (#41474) (#41522)
Browse files Browse the repository at this point in the history
Migrates to use state-api, to unify behavior with CLI and UI
Delete log_proxy API, it's legacy and has some security issues.

---------

Signed-off-by: Alan Guo <[email protected]>
Co-authored-by: Alan Guo <[email protected]>
  • Loading branch information
pcmoritz and alanwguo authored Nov 30, 2023
1 parent 7459639 commit 82a8df1
Show file tree
Hide file tree
Showing 23 changed files with 556 additions and 614 deletions.
23 changes: 8 additions & 15 deletions dashboard/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { JobDetailInfoPage } from "./pages/job/JobDetailInfoPage";
import { JobDetailLayout, JobPage } from "./pages/job/JobDetailLayout";
import { MainNavLayout } from "./pages/layout/MainNavLayout";
import { SideTabPage } from "./pages/layout/SideTabLayout";
import { LogsLayout } from "./pages/log/Logs";
import {
LogsLayout,
StateApiLogsListPage,
StateApiLogViewerPage,
} from "./pages/log/Logs";
import { Metrics } from "./pages/metrics";
import { DashboardUids, getMetricsInfo } from "./pages/metrics/utils";
import Nodes, { ClusterMainPageLayout } from "./pages/node";
Expand Down Expand Up @@ -50,13 +54,11 @@ dayjs.extend(duration);
// lazy loading fro prevent loading too much code at once
const Actors = React.lazy(() => import("./pages/actor"));
const CMDResult = React.lazy(() => import("./pages/cmd/CMDResult"));
const Logs = React.lazy(() => import("./pages/log/Logs"));

// a global map for relations
export type GlobalContextType = {
nodeMap: { [key: string]: string };
nodeMapByIp: { [key: string]: string };
ipLogMap: { [key: string]: string };
namespaceMap: { [key: string]: string[] };
/**
* Whether the initial metrics context has been fetched or not.
Expand Down Expand Up @@ -89,7 +91,6 @@ export type GlobalContextType = {
export const GlobalContext = React.createContext<GlobalContextType>({
nodeMap: {},
nodeMapByIp: {},
ipLogMap: {},
namespaceMap: {},
metricsContextLoaded: false,
grafanaHost: undefined,
Expand All @@ -103,7 +104,6 @@ const App = () => {
const [context, setContext] = useState<GlobalContextType>({
nodeMap: {},
nodeMapByIp: {},
ipLogMap: {},
namespaceMap: {},
metricsContextLoaded: false,
grafanaHost: undefined,
Expand All @@ -117,17 +117,14 @@ const App = () => {
if (res?.data?.data?.summary) {
const nodeMap = {} as { [key: string]: string };
const nodeMapByIp = {} as { [key: string]: string };
const ipLogMap = {} as { [key: string]: string };
res.data.data.summary.forEach(({ hostname, raylet, ip, logUrl }) => {
res.data.data.summary.forEach(({ hostname, raylet, ip }) => {
nodeMap[hostname] = raylet.nodeId;
nodeMapByIp[ip] = raylet.nodeId;
ipLogMap[ip] = logUrl;
});
setContext((existingContext) => ({
...existingContext,
nodeMap,
nodeMapByIp,
ipLogMap,
namespaceMap: {},
}));
}
Expand Down Expand Up @@ -285,12 +282,8 @@ const App = () => {
</Route>
</Route>
<Route element={<LogsLayout />} path="logs">
{/* TODO(aguo): Refactor Logs component to use optional query
params since react-router 6 doesn't support optional path params... */}
<Route element={<Logs />} path="" />
<Route element={<Logs />} path=":host">
<Route element={<Logs />} path=":path" />
</Route>
<Route element={<StateApiLogsListPage />} path="" />
<Route element={<StateApiLogViewerPage />} path="viewer" />
</Route>
</Route>
<Route element={<CMDResult />} path="/cmd/:cmd/:ip/:pid" />
Expand Down
54 changes: 25 additions & 29 deletions dashboard/client/src/components/ActorTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import { SearchOutlined } from "@material-ui/icons";
import Autocomplete from "@material-ui/lab/Autocomplete";
import Pagination from "@material-ui/lab/Pagination";
import _ from "lodash";
import React, { useContext, useMemo, useState } from "react";
import { GlobalContext } from "../App";
import React, { useMemo, useState } from "react";
import { DurationText } from "../common/DurationText";
import { ActorLink } from "../common/links";
import { CpuProfilingLink, CpuStackTraceLink } from "../common/ProfilingLink";
Expand Down Expand Up @@ -89,7 +88,6 @@ const ActorTable = ({
});
const [actorIdFilterValue, setActorIdFilterValue] = useState(filterToActorId);
const [pageSize, setPageSize] = useState(10);
const { ipLogMap } = useContext(GlobalContext);

//We get a filtered and sorted actor list to render from prop actors
const sortedActors = useMemo(() => {
Expand Down Expand Up @@ -427,32 +425,30 @@ const ActorTable = ({
<StatusChip type="actor" status={state} />
</TableCell>
<TableCell align="center">
{ipLogMap[address?.ipAddress] && (
<React.Fragment>
<ActorLink
actorId={actorId}
to={
detailPathPrefix
? `${detailPathPrefix}/${actorId}`
: actorId
}
>
Log
</ActorLink>
<br />
<CpuProfilingLink
pid={pid}
ip={address?.ipAddress}
type=""
/>
<br />
<CpuStackTraceLink
pid={pid}
ip={address?.ipAddress}
type=""
/>
</React.Fragment>
)}
<React.Fragment>
<ActorLink
actorId={actorId}
to={
detailPathPrefix
? `${detailPathPrefix}/${actorId}`
: actorId
}
>
Log
</ActorLink>
<br />
<CpuProfilingLink
pid={pid}
ip={address?.ipAddress}
type=""
/>
<br />
<CpuStackTraceLink
pid={pid}
ip={address?.ipAddress}
type=""
/>
</React.Fragment>
</TableCell>
<TableCell align="center">
{startTime && startTime > 0 ? (
Expand Down
12 changes: 6 additions & 6 deletions dashboard/client/src/components/WorkerTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const RayletWorkerTable = ({
}) => {
const { changeFilter, filterFunc } = useFilter();
const [key, setKey] = useState("");
const { nodeMapByIp, ipLogMap } = useContext(GlobalContext);
const { nodeMapByIp } = useContext(GlobalContext);
const open = () => setKey(`ON${Math.random()}`);
const close = () => setKey(`OFF${Math.random()}`);

Expand Down Expand Up @@ -227,13 +227,13 @@ const RayletWorkerTable = ({
</TableCell>
<TableCell align="center">
<Grid container spacing={2}>
{ipLogMap[coreWorkerStats[0]?.ipAddress] && (
{coreWorkerStats[0] && (
<Grid item>
<Link
target="_blank"
to={`/logs/${encodeURIComponent(
ipLogMap[coreWorkerStats[0]?.ipAddress],
)}?fileName=${
to={`/logs/?nodeId=${encodeURIComponent(
nodeMapByIp[coreWorkerStats[0].ipAddress],
)}&fileName=${
coreWorkerStats[0].jobId || ""
}-${pid}`}
>
Expand Down Expand Up @@ -282,7 +282,7 @@ const RayletWorkerTable = ({
{nodeMapByIp[coreWorkerStats[0]?.ipAddress] ? (
<Link
target="_blank"
to={`/node/${
to={`/cluster/nodes/${
nodeMapByIp[coreWorkerStats[0]?.ipAddress]
}`}
>
Expand Down
18 changes: 9 additions & 9 deletions dashboard/client/src/pages/job/JobDriverLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ export const JobDriverLogs = ({ job }: JobDriverLogsProps) => {
? `job-driver-${submission_id}.log`
: undefined;

const { ipLogMap } = useContext(GlobalContext);
const { nodeMapByIp } = useContext(GlobalContext);

let link: string | undefined;

if (job.driver_agent_http_address) {
link = `/logs/${encodeURIComponent(
`${job.driver_agent_http_address}/logs`,
)}`;
} else if (job.driver_info && ipLogMap[job.driver_info.node_ip_address]) {
link = `/logs/${encodeURIComponent(
ipLogMap[job.driver_info.node_ip_address],
if (job.driver_node_id) {
link = `/logs/?nodeId=${encodeURIComponent(job.driver_node_id)}`;
} else if (job.driver_info?.node_id) {
link = `/logs/?nodeId=${encodeURIComponent(job.driver_info.node_id)}`;
} else if (job.driver_info?.node_ip_address) {
link = `/logs/?nodeId=${encodeURIComponent(
nodeMapByIp[job.driver_info.node_ip_address],
)}`;
}

if (link && job.job_id) {
link += `?fileName=${job.job_id}`;
link += `&fileName=${job.job_id}`;
} else {
// Don't show "other logs" link if link is not available
// or job_id does not exist.
Expand Down
5 changes: 1 addition & 4 deletions dashboard/client/src/pages/job/hook/useJobDetail.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { useContext, useState } from "react";
import { useState } from "react";
import { useParams } from "react-router-dom";
import useSWR from "swr";
import { GlobalContext } from "../../../App";
import { API_REFRESH_INTERVAL_MS } from "../../../common/constants";
import { getJobDetail } from "../../../service/job";

export const useJobDetail = () => {
const params = useParams() as { id: string };
const [msg, setMsg] = useState("Loading the job detail");
const [refreshing, setRefresh] = useState(true);
const { ipLogMap } = useContext(GlobalContext);
const { data: job, isLoading } = useSWR(
["useJobDetail", params.id],
async ([_, jobId]) => {
Expand All @@ -29,6 +27,5 @@ export const useJobDetail = () => {
isLoading,
msg,
params,
ipLogMap,
};
};
5 changes: 1 addition & 4 deletions dashboard/client/src/pages/job/hook/useJobList.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useContext, useRef, useState } from "react";
import { useRef, useState } from "react";
import useSWR from "swr";
import { GlobalContext } from "../../../App";
import { API_REFRESH_INTERVAL_MS } from "../../../common/constants";
import { getJobList } from "../../../service/job";

export const useJobList = () => {
const [page, setPage] = useState({ pageSize: 10, pageNo: 1 });
const [msg, setMsg] = useState("Loading the job list...");
const [isRefreshing, setRefresh] = useState(true);
const { ipLogMap } = useContext(GlobalContext);
const [filter, setFilter] = useState<
{
key: "job_id" | "status";
Expand Down Expand Up @@ -59,6 +57,5 @@ export const useJobList = () => {
page,
originalJobs: jobList,
setPage: (key: string, val: number) => setPage({ ...page, [key]: val }),
ipLogMap,
};
};
Loading

0 comments on commit 82a8df1

Please sign in to comment.