diff --git a/dashboard/client/src/common/CodeDialogButton/CodeDialogButton.tsx b/dashboard/client/src/common/CodeDialogButton/CodeDialogButton.tsx index 711616fa1092..0aea9c9f4d61 100644 --- a/dashboard/client/src/common/CodeDialogButton/CodeDialogButton.tsx +++ b/dashboard/client/src/common/CodeDialogButton/CodeDialogButton.tsx @@ -5,9 +5,11 @@ import { makeStyles, Typography, } from "@material-ui/core"; +import classNames from "classnames"; import yaml from "js-yaml"; import React, { useState } from "react"; import DialogWithTitle from "../DialogWithTitle"; +import { ClassNameProps } from "../props"; const useStyles = makeStyles((theme) => createStyles({ @@ -15,7 +17,8 @@ const useStyles = makeStyles((theme) => whiteSpace: "pre", fontFamily: "SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace", padding: theme.spacing(2), - // borderRadius: theme.spacing(1), + overflow: "scroll", + maxHeight: 600, }, }), ); @@ -92,13 +95,14 @@ const useCodeDialogButtonWithPreviewStyles = makeStyles((theme) => }), ); -type CodeDialogButtonWithPreviewProps = CodeDialogButtonProps; +type CodeDialogButtonWithPreviewProps = CodeDialogButtonProps & ClassNameProps; /** * Similar to CodeDialogButton but also shows a snippet of the expanded text next to the button. */ export const CodeDialogButtonWithPreview = ({ code, buttonText, + className, ...props }: CodeDialogButtonWithPreviewProps) => { const classes = useCodeDialogButtonWithPreviewStyles(); @@ -109,7 +113,7 @@ export const CodeDialogButtonWithPreview = ({ const buttonTextToPass = buttonText ?? "Expand"; return ( -
+
{codeText} + createStyles({ + contentContainer: { + padding: theme.spacing(2), + height: "100%", + }, + }), +); + +type SectionProps = { + title?: string; +} & ClassNameProps & + BoxProps; + +export const Section = ({ + title, + children, + className, + ...props +}: PropsWithChildren) => { + const classes = useStyles(); + + return ( + + {title && ( + + {title} + + )} + + {children} + + + ); +}; diff --git a/dashboard/client/src/components/MetadataSection/MetadataSection.tsx b/dashboard/client/src/components/MetadataSection/MetadataSection.tsx index eb2c8af78b50..91c73dc7723d 100644 --- a/dashboard/client/src/components/MetadataSection/MetadataSection.tsx +++ b/dashboard/client/src/components/MetadataSection/MetadataSection.tsx @@ -4,7 +4,6 @@ import { IconButton, Link, makeStyles, - Paper, Tooltip, Typography, } from "@material-ui/core"; @@ -12,6 +11,7 @@ import copy from "copy-to-clipboard"; import React, { useState } from "react"; import { RiFileCopyLine } from "react-icons/ri"; import { Link as RouterLink } from "react-router-dom"; +import { Section } from "../../common/Section"; import { HelpInfo } from "../Tooltip"; export type StringOnlyMetadataContent = { @@ -55,7 +55,6 @@ const useStyles = makeStyles((theme) => gridTemplateColumns: "repeat(3, minmax(0, 1fr))", rowGap: theme.spacing(1), columnGap: theme.spacing(4), - padding: theme.spacing(2), }, label: { color: theme.palette.text.secondary, @@ -193,15 +192,8 @@ export const MetadataSection = ({ metadataList: Metadata[]; }) => { return ( - - {header && ( - - {header} - - )} - - - - +
+ +
); }; diff --git a/dashboard/client/src/pages/job/JobDetail.tsx b/dashboard/client/src/pages/job/JobDetail.tsx index e3721403dbc7..3e630c5e1c6f 100644 --- a/dashboard/client/src/pages/job/JobDetail.tsx +++ b/dashboard/client/src/pages/job/JobDetail.tsx @@ -1,31 +1,41 @@ -import { Box, Grid, makeStyles, Typography } from "@material-ui/core"; +import { Box, makeStyles, Typography } from "@material-ui/core"; import React, { useContext, useRef, useState } from "react"; import { Link } from "react-router-dom"; import { GlobalContext } from "../../App"; import { CollapsibleSection } from "../../common/CollapsibleSection"; -import { DurationText } from "../../common/DurationText"; -import { formatDateFromTimeMs } from "../../common/formatUtils"; -import { - CpuProfilingLink, - CpuStackTraceLink, -} from "../../common/ProfilingLink"; +import { Section } from "../../common/Section"; import Loading from "../../components/Loading"; -import { MetadataSection } from "../../components/MetadataSection"; import { StatusChip } from "../../components/StatusChip"; import TitleCard from "../../components/TitleCard"; import { NestedJobProgressLink, UnifiedJob } from "../../type/job"; import ActorList from "../actor/ActorList"; +import { NodeCountCard } from "../overview/cards/NodeCountCard"; import PlacementGroupList from "../state/PlacementGroup"; import TaskList from "../state/task"; import { useRayStatus } from "./hook/useClusterStatus"; import { useJobDetail } from "./hook/useJobDetail"; +import { JobMetadataSection } from "./JobDetailInfoPage"; +import { JobDriverLogs } from "./JobDriverLogs"; import { JobProgressBar } from "./JobProgressBar"; import { TaskTimeline } from "./TaskTimeline"; const useStyle = makeStyles((theme) => ({ root: { padding: theme.spacing(2), + backgroundColor: "white", + }, + section: { + marginBottom: theme.spacing(4), + }, + autoscalerSection: { + flexWrap: "wrap", + [theme.breakpoints.up("md")]: { + flexWrap: "nowrap", + }, + }, + nodeCountCard: { + flex: "1 0 500px", }, })); @@ -71,23 +81,21 @@ export const JobDetailChartsPage = () => { return (
- - {title} - + + {title} + {cluster_status_rows.map((i, key) => { // Format the output. // See format_info_string in util.py - if (i.startsWith("-----") || i.startsWith("=====")) { - // Separator - return
; + if (i.startsWith("-----") || i.startsWith("=====") || i === "") { + // Ignore separators + return null; } else if (i.endsWith(":")) { return (
{i}
); - } else if (i === "") { - return
; } else { return
{i}
; } @@ -145,174 +153,132 @@ export const JobDetailChartsPage = () => { return (
- - , - }, - { - label: "Job ID", - content: job.job_id - ? { - value: job.job_id, - copyableValue: job.job_id, - } - : { value: "-" }, - }, - { - label: "Submission ID", - content: job.submission_id - ? { - value: job.submission_id, - copyableValue: job.submission_id, - } - : { - value: "-", - }, - }, - { - label: "Duration", - content: job.start_time ? ( - - ) : ( - - - ), - }, - { - label: "Started at", - content: { - value: job.start_time - ? formatDateFromTimeMs(job.start_time) - : "-", - }, - }, - { - label: "Ended at", - content: { - value: job.end_time ? formatDateFromTimeMs(job.end_time) : "-", - }, - }, - { - label: "Actions", - content: ( -
- -
- -
- -
- ), - }, - ]} - /> -
- - - - - - - - - + + + +
+ +
+
+ + {job.type === "SUBMISSION" && ( + +
+ +
+
+ )} + + +
+ +
+
+ + + + +
{cluster_status?.data ? formatNodeStatus(cluster_status?.data.clusterStatus) : "No cluster status."} - - - - +
+
{cluster_status?.data ? formatResourcesStatus(cluster_status?.data.clusterStatus) : "No cluster status."} - - - - - { - setTaskTableExpanded(!taskTableExpanded); - }} - > +
+
+
+ + { + setTaskTableExpanded(!taskTableExpanded); + }} + className={classes.section} + > +
- - - - { - setActorTableExpanded(!actorTableExpanded); - }} - > +
+
+ + { + setActorTableExpanded(!actorTableExpanded); + }} + className={classes.section} + > +
- - - - +
+
+ + +
- - +
+
); }; diff --git a/dashboard/client/src/pages/job/JobDetailActorPage.tsx b/dashboard/client/src/pages/job/JobDetailActorPage.tsx index b31d653ca3ed..2a8ab3d9e1a6 100644 --- a/dashboard/client/src/pages/job/JobDetailActorPage.tsx +++ b/dashboard/client/src/pages/job/JobDetailActorPage.tsx @@ -1,7 +1,7 @@ import { makeStyles } from "@material-ui/core"; import React from "react"; -import TitleCard from "../../components/TitleCard"; +import { Section } from "../../common/Section"; import ActorList from "../actor/ActorList"; import { MainNavPageInfo } from "../layout/mainNavContext"; import { useJobDetail } from "./hook/useJobDetail"; @@ -9,6 +9,7 @@ import { useJobDetail } from "./hook/useJobDetail"; const useStyle = makeStyles((theme) => ({ root: { padding: theme.spacing(2), + backgroundColor: "white", }, })); @@ -31,9 +32,9 @@ export const JobDetailActorsPage = () => { return (
- +
- +
); }; diff --git a/dashboard/client/src/pages/job/JobDetailInfoPage.tsx b/dashboard/client/src/pages/job/JobDetailInfoPage.tsx index b8345e8d44e4..2d69d75d0d11 100644 --- a/dashboard/client/src/pages/job/JobDetailInfoPage.tsx +++ b/dashboard/client/src/pages/job/JobDetailInfoPage.tsx @@ -1,18 +1,26 @@ -import { makeStyles } from "@material-ui/core"; +import { createStyles, makeStyles, Typography } from "@material-ui/core"; import React from "react"; +import { CodeDialogButtonWithPreview } from "../../common/CodeDialogButton"; import { DurationText } from "../../common/DurationText"; import { formatDateFromTimeMs } from "../../common/formatUtils"; +import { + CpuProfilingLink, + CpuStackTraceLink, +} from "../../common/ProfilingLink"; import Loading from "../../components/Loading"; import { MetadataSection } from "../../components/MetadataSection"; import { StatusChip } from "../../components/StatusChip"; import TitleCard from "../../components/TitleCard"; +import { UnifiedJob } from "../../type/job"; import { MainNavPageInfo } from "../layout/mainNavContext"; import { useJobDetail } from "./hook/useJobDetail"; +import { JobLogsLink } from "./JobDetail"; const useStyle = makeStyles((theme) => ({ root: { padding: theme.spacing(2), + backgroundColor: "white", }, })); @@ -51,70 +59,120 @@ export const JobDetailInfoPage = () => { path: job.job_id ? `/jobs/${job.job_id}/info` : undefined, }} /> - - , - }, - { - label: "Job ID", - content: job.job_id - ? { - value: job.job_id, - copyableValue: job.job_id, - } - : { value: "-" }, - }, - { - label: "Submission ID", - content: job.submission_id - ? { - value: job.submission_id, - copyableValue: job.submission_id, - } - : { - value: "-", - }, - }, - { - label: "Duration", - content: job.start_time ? ( - - ) : ( - - - ), - }, - { - label: "Started at", - content: { - value: job.start_time - ? formatDateFromTimeMs(job.start_time) - : "-", + {job.job_id} + +
+ ); +}; + +const useJobMetadataSectionStyles = makeStyles((theme) => + createStyles({ + metadataButton: { + display: "inline-flex", + maxWidth: "100%", + }, + }), +); + +type JobMetadataSectionProps = { + job: UnifiedJob; +}; + +export const JobMetadataSection = ({ job }: JobMetadataSectionProps) => { + const classes = useJobMetadataSectionStyles(); + + return ( + , + }, + { + label: "Job ID", + content: job.job_id + ? { + value: job.job_id, + copyableValue: job.job_id, + } + : { value: "-" }, + }, + { + label: "Submission ID", + content: job.submission_id + ? { + value: job.submission_id, + copyableValue: job.submission_id, + } + : { + value: "-", }, - }, - { - label: "Ended at", - content: { - value: job.end_time ? formatDateFromTimeMs(job.end_time) : "-", + }, + { + label: "Duration", + content: job.start_time ? ( + + ) : ( + - + ), + }, + { + label: "Started at", + content: { + value: job.start_time ? formatDateFromTimeMs(job.start_time) : "-", + }, + }, + { + label: "Ended at", + content: { + value: job.end_time ? formatDateFromTimeMs(job.end_time) : "-", + }, + }, + ...(job.type === "SUBMISSION" + ? [ + { + label: "User-provided metadata", + content: + job.metadata && Object.keys(job.metadata).length ? ( + + ) : undefined, }, - }, - ]} - /> - -
+ ] + : []), + { + label: "Actions", + content: ( +
+ +
+ +
+ +
+ ), + }, + ]} + /> ); }; diff --git a/dashboard/client/src/pages/job/JobDriverLogs.component.test.tsx b/dashboard/client/src/pages/job/JobDriverLogs.component.test.tsx new file mode 100644 index 000000000000..1c526b1caccf --- /dev/null +++ b/dashboard/client/src/pages/job/JobDriverLogs.component.test.tsx @@ -0,0 +1,47 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; +import { get } from "../../service/requestHandlers"; +import { JobDriverLogs } from "./JobDriverLogs"; + +jest.mock("../../service/requestHandlers"); + +const mockedGet = jest.mocked(get); + +describe("JobDriverLogs", () => { + it("renders", async () => { + expect.assertions(6); + + mockedGet.mockResolvedValue({ + headers: { + "content-type": "text/plain", + }, + data: "log line\nthis is a line\nHi\n10\nfoo", + }); + + render( + , + ); + + await screen.findByText(/log line/); + expect(screen.getByText(/log line/)).toBeVisible(); + expect(screen.getByText(/this is a line/)).toBeVisible(); + expect(screen.getByText(/Hi/)).toBeVisible(); + expect(screen.getByText(/10/)).toBeVisible(); + expect(screen.getByText(/foo/)).toBeVisible(); + + expect(mockedGet).toBeCalledWith( + "log_proxy?url=http%3A%2F%2F127.0.0.1%3A52365%2Flogs%2Fjob-driver-raysubmit_12345.log", + ); + }); +}); diff --git a/dashboard/client/src/pages/job/JobDriverLogs.tsx b/dashboard/client/src/pages/job/JobDriverLogs.tsx new file mode 100644 index 000000000000..5d45b03bf1bb --- /dev/null +++ b/dashboard/client/src/pages/job/JobDriverLogs.tsx @@ -0,0 +1,78 @@ +import { Typography } from "@material-ui/core"; +import React, { useContext } from "react"; +import useSWR from "swr"; +import { GlobalContext } from "../../App"; +import { getLogDetail, getLogDownloadUrl } from "../../service/log"; +import { UnifiedJob } from "../../type/job"; +import { LogViewer } from "../log/LogViewer"; + +const useDriverLogs = ( + job: Pick< + UnifiedJob, + "driver_agent_http_address" | "driver_info" | "submission_id" + >, +) => { + const { ipLogMap } = useContext(GlobalContext); + const { driver_agent_http_address, driver_info, submission_id } = job; + const host = (() => { + if (driver_agent_http_address) { + return `${driver_agent_http_address}/logs/`; + } else if (driver_info && ipLogMap[driver_info.node_ip_address]) { + return `${ipLogMap[driver_info.node_ip_address]}/`; + } + })(); + const path = `job-driver-${submission_id}.log`; + + const url = host ? `${host}${path}` : undefined; + const downloadUrl = url ? getLogDownloadUrl(url) : undefined; + + const { + data: log, + isLoading, + mutate, + } = useSWR(url ? ["useDriverLogs", url] : null, async ([_, url]) => + getLogDetail(url) + .then((res) => { + if (res) { + return res; + } else { + return "(This file is empty.)"; + } + }) + .catch(() => { + return "(Failed to load)"; + }), + ); + + return { + log: isLoading ? "Loading..." : log, + downloadUrl, + refresh: mutate, + host, + path, + }; +}; + +type JobDriverLogsProps = { + job: Pick< + UnifiedJob, + "driver_agent_http_address" | "driver_info" | "submission_id" + >; +}; + +export const JobDriverLogs = ({ job }: JobDriverLogsProps) => { + const { downloadUrl, log, path, refresh } = useDriverLogs(job); + return typeof log === "string" ? ( + { + refresh(); + }} + /> + ) : ( + Failed to load + ); +}; diff --git a/dashboard/client/src/pages/job/TaskTimeline.tsx b/dashboard/client/src/pages/job/TaskTimeline.tsx index 50e7fd2b3357..a3129636cb3f 100644 --- a/dashboard/client/src/pages/job/TaskTimeline.tsx +++ b/dashboard/client/src/pages/job/TaskTimeline.tsx @@ -10,9 +10,6 @@ import { ClassNameProps } from "../../common/props"; import { downloadTaskTimelineHref } from "../../service/task"; const useStyle = makeStyles((theme) => ({ - root: { - padding: theme.spacing(2, 0, 0), - }, button: { marginTop: theme.spacing(2), }, @@ -26,7 +23,7 @@ export const TaskTimeline = ({ jobId }: TaskTimelineProps) => { const classes = useStyle(); return ( -
+
{/* TODO(aguo): Add link to external documentation about Timeline view. */} Timeline view shows how tasks are executed across different nodes and diff --git a/dashboard/client/src/pages/job/hook/useJobDetail.ts b/dashboard/client/src/pages/job/hook/useJobDetail.ts index d473eb3cba31..9c31ab4ad1a0 100644 --- a/dashboard/client/src/pages/job/hook/useJobDetail.ts +++ b/dashboard/client/src/pages/job/hook/useJobDetail.ts @@ -11,10 +11,10 @@ export const useJobDetail = () => { const [refreshing, setRefresh] = useState(true); const { ipLogMap } = useContext(GlobalContext); const { data: job, isLoading } = useSWR( - "useJobDetail", - async () => { + ["useJobDetail", params.id], + async ([_, jobId]) => { try { - const rsp = await getJobDetail(params.id); + const rsp = await getJobDetail(jobId); return rsp.data; } catch (e) { setMsg("Job Query Error Please Check JobId"); diff --git a/dashboard/client/src/pages/log/LogViewer.tsx b/dashboard/client/src/pages/log/LogViewer.tsx new file mode 100644 index 000000000000..cb155b9417c7 --- /dev/null +++ b/dashboard/client/src/pages/log/LogViewer.tsx @@ -0,0 +1,195 @@ +import { + Button, + createStyles, + InputAdornment, + LinearProgress, + makeStyles, + Switch, + TextField, +} from "@material-ui/core"; +import { SearchOutlined } from "@material-ui/icons"; +import React, { useState } from "react"; +import LogVirtualView from "../../components/LogView/LogVirtualView"; + +const useStyles = makeStyles((theme) => + createStyles({ + search: { + margin: theme.spacing(1), + }, + }), +); + +const useLogViewer = () => { + const [search, setSearch] = + useState<{ + keywords?: string; + lineNumber?: string; + fontSize?: number; + revert?: boolean; + }>(); + const [startTime, setStart] = useState(); + const [endTime, setEnd] = useState(); + + return { + search, + setSearch, + startTime, + setStart, + endTime, + setEnd, + }; +}; + +type LogViewerProps = { + path?: string; + log: string; + downloadUrl?: string; + onRefreshClick?: () => void; + height?: number; +}; + +export const LogViewer = ({ + path, + log, + downloadUrl, + onRefreshClick, + height = 600, +}: LogViewerProps) => { + const classes = useStyles(); + + const { search, setSearch, startTime, setStart, endTime, setEnd } = + useLogViewer(); + + return ( + + {log !== "Loading..." && ( +
+
+ { + setSearch({ ...search, keywords: value }); + }, + type: "", + endAdornment: ( + + + + ), + }} + /> + { + setSearch({ ...search, lineNumber: value }); + }, + type: "", + endAdornment: ( + + + + ), + }} + /> + { + setSearch({ ...search, fontSize: Number(value) }); + }, + type: "", + }} + /> + { + setStart(val.target.value); + }} + InputLabelProps={{ + shrink: true, + }} + /> + { + setEnd(val.target.value); + }} + InputLabelProps={{ + shrink: true, + }} + /> +
+ Reverse:{" "} + setSearch({ ...search, revert: v })} + /> + {onRefreshClick && ( + + )} + + {downloadUrl && path && ( + + )} +
+
+ +
+ )} + {log === "Loading..." && ( +
+
+ +
+ )} +
+ ); +}; diff --git a/dashboard/client/src/pages/log/Logs.tsx b/dashboard/client/src/pages/log/Logs.tsx index a44253595a51..61eb93468840 100644 --- a/dashboard/client/src/pages/log/Logs.tsx +++ b/dashboard/client/src/pages/log/Logs.tsx @@ -1,22 +1,11 @@ -import { - Button, - InputAdornment, - LinearProgress, - List, - ListItem, - makeStyles, - Paper, - Switch, - TextField, -} from "@material-ui/core"; -import { SearchOutlined } from "@material-ui/icons"; -import React, { useEffect, useRef, useState } from "react"; +import { Button, List, ListItem, makeStyles, Paper } from "@material-ui/core"; +import React, { useEffect, useState } from "react"; import { Outlet, useLocation, useParams } from "react-router-dom"; -import LogVirtualView from "../../components/LogView/LogVirtualView"; import { SearchInput } from "../../components/SearchComponent"; import TitleCard from "../../components/TitleCard"; import { getLogDetail, getLogDownloadUrl } from "../../service/log"; import { MainNavPageInfo } from "../layout/mainNavContext"; +import { LogViewer } from "./LogViewer"; const useStyles = makeStyles((theme) => ({ root: { @@ -36,30 +25,16 @@ const useStyles = makeStyles((theme) => ({ }, })); -type LogsProps = { - theme?: "dark" | "light"; -}; - -const useLogs = ({ theme }: LogsProps) => { +const useLogs = () => { const { search: urlSearch } = useLocation(); const { host, path } = useParams(); const searchMap = new URLSearchParams(urlSearch); const urlFileName = searchMap.get("fileName"); - const el = useRef(null); const [origin, setOrigin] = useState(); - const [search, setSearch] = - useState<{ - keywords?: string; - lineNumber?: string; - fontSize?: number; - revert?: boolean; - }>(); const [fileName, setFileName] = useState(searchMap.get("fileName") || ""); const [log, setLogs] = useState(); const [downloadUrl, setDownloadUrl] = useState(); - const [startTime, setStart] = useState(); - const [endTime, setEnd] = useState(); useEffect(() => { setFileName(urlFileName || ""); @@ -97,37 +72,14 @@ const useLogs = ({ theme }: LogsProps) => { downloadUrl, host, path, - el, - search, - setSearch, - theme, fileName, setFileName, - startTime, - setStart, - endTime, - setEnd, }; }; -const Logs = (props: LogsProps) => { +const Logs = () => { const classes = useStyles(); - const { - log, - origin, - downloadUrl, - path, - el, - search, - setSearch, - theme, - fileName, - setFileName, - startTime, - setStart, - endTime, - setEnd, - } = useLogs(props); + const { log, origin, downloadUrl, path, fileName, setFileName } = useLogs(); let href = "#/logs/"; if (origin) { @@ -142,7 +94,7 @@ const Logs = (props: LogsProps) => { } } return ( -
+
{!origin &&

Select a node to view logs

} @@ -191,125 +143,8 @@ const Logs = (props: LogsProps) => { ))} )} - {typeof log === "string" && log !== "Loading..." && ( -
-
- { - setSearch({ ...search, keywords: value }); - }, - type: "", - endAdornment: ( - - - - ), - }} - /> - { - setSearch({ ...search, lineNumber: value }); - }, - type: "", - endAdornment: ( - - - - ), - }} - /> - { - setSearch({ ...search, fontSize: Number(value) }); - }, - type: "", - }} - /> - { - setStart(val.target.value); - }} - InputLabelProps={{ - shrink: true, - }} - /> - { - setEnd(val.target.value); - }} - InputLabelProps={{ - shrink: true, - }} - /> -
- Reverse:{" "} - setSearch({ ...search, revert: v })} - /> - - {downloadUrl && path && ( - - )} -
-
- -
- )} - {log === "Loading..." && ( -
-
- -
+ {typeof log === "string" && ( + )}
diff --git a/dashboard/client/src/pages/node/ClusterLayout.tsx b/dashboard/client/src/pages/node/ClusterLayout.tsx index c74f6d7a3909..16a02d05eb97 100644 --- a/dashboard/client/src/pages/node/ClusterLayout.tsx +++ b/dashboard/client/src/pages/node/ClusterLayout.tsx @@ -1,17 +1,12 @@ import React from "react"; -import { RiInformationLine, RiTableAltLine } from "react-icons/ri"; +import { RiInformationLine, RiTableLine } from "react-icons/ri"; import { SideTabLayout, SideTabRouteLink } from "../layout/SideTabLayout"; export const ClusterLayout = () => { return ( - + ); }; diff --git a/dashboard/client/src/theme.ts b/dashboard/client/src/theme.ts index 6259f9b65b22..a3ec06564f61 100644 --- a/dashboard/client/src/theme.ts +++ b/dashboard/client/src/theme.ts @@ -17,10 +17,12 @@ const basicTheme: ThemeOptions = { '"Segoe UI Symbol"', ].join(","), h1: { - fontSize: "2rem", + fontSize: "1.5rem", + fontWeight: 500, }, h2: { - fontSize: "1.5rem", + fontSize: "1.25rem", + fontWeight: 500, }, h3: { fontSize: "1rem", diff --git a/dashboard/modules/metrics/dashboards/serve_deployment_grafana_dashboard_base.json b/dashboard/modules/metrics/dashboards/serve_deployment_grafana_dashboard_base.json index c66c41e5d50e..af9c611e8867 100644 --- a/dashboard/modules/metrics/dashboards/serve_deployment_grafana_dashboard_base.json +++ b/dashboard/modules/metrics/dashboards/serve_deployment_grafana_dashboard_base.json @@ -131,6 +131,7 @@ } ] }, + "rayMeta": ["excludesSystemRoutes"], "time": { "from": "now-30m", "to": "now"