Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dashboard] Enable customizable refresh frequency for the Metrics page #44037

Merged
merged 10 commits into from
Jun 20, 2024
86 changes: 84 additions & 2 deletions dashboard/client/src/pages/metrics/Metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Alert,
AlertProps,
Button,
InputAdornment,
Link,
Menu,
MenuItem,
Expand All @@ -12,11 +13,13 @@ import {
import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles";
import React, { useContext, useEffect, useState } from "react";
import { BiRefresh, BiTime } from "react-icons/bi";
import { RiExternalLinkLine } from "react-icons/ri";

import { GlobalContext } from "../../App";
import { CollapsibleSection } from "../../common/CollapsibleSection";
import { ClassNameProps } from "../../common/props";
import { HelpInfo } from "../../components/Tooltip";
import { MainNavPageInfo } from "../layout/mainNavContext";
import { MAIN_NAV_HEIGHT } from "../layout/MainNavLayout";

Expand Down Expand Up @@ -45,6 +48,20 @@ const useStyles = makeStyles((theme) =>
}),
);

export enum RefreshOptions {
OFF = "off",
FIVE_SECONDS = "5s",
TEN_SECONDS = "10s",
THIRTY_SECONDS = "30s",
ONE_MIN = "1m",
FIVE_MINS = "5m",
FIFTEEN_MINS = "15m",
THIRTY_MINS = "30m",
ONE_HOUR = "1h",
TWO_HOURS = "2h",
ONE_DAY = "1d",
}

export enum TimeRangeOptions {
FIVE_MINS = "Last 5 minutes",
THIRTY_MINS = "Last 30 minutes",
Expand All @@ -57,6 +74,20 @@ export enum TimeRangeOptions {
SEVEN_DAYS = "Last 7 days",
}

export const REFRESH_VALUE: Record<RefreshOptions, string> = {
[RefreshOptions.OFF]: "",
[RefreshOptions.FIVE_SECONDS]: "5s",
[RefreshOptions.TEN_SECONDS]: "10s",
[RefreshOptions.THIRTY_SECONDS]: "30s",
[RefreshOptions.ONE_MIN]: "1m",
[RefreshOptions.FIVE_MINS]: "5m",
[RefreshOptions.FIFTEEN_MINS]: "15m",
[RefreshOptions.THIRTY_MINS]: "30m",
[RefreshOptions.ONE_HOUR]: "1h",
[RefreshOptions.TWO_HOURS]: "2h",
[RefreshOptions.ONE_DAY]: "1d",
};

export const TIME_RANGE_TO_FROM_VALUE: Record<TimeRangeOptions, string> = {
[TimeRangeOptions.FIVE_MINS]: "now-5m",
[TimeRangeOptions.THIRTY_MINS]: "now-30m",
Expand Down Expand Up @@ -358,13 +389,25 @@ export const Metrics = () => {

const grafanaDefaultDatasource = dashboardDatasource ?? "Prometheus";

const [refreshOption, setRefreshOption] = useState<RefreshOptions>(
RefreshOptions.FIVE_SECONDS,
);

const [timeRangeOption, setTimeRangeOption] = useState<TimeRangeOptions>(
TimeRangeOptions.FIVE_MINS,
);

const [refresh, setRefresh] = useState<string | null>(null);

const [[from, to], setTimeRange] = useState<[string | null, string | null]>([
null,
null,
]);

useEffect(() => {
setRefresh(REFRESH_VALUE[refreshOption]);
}, [refreshOption]);

useEffect(() => {
const from = TIME_RANGE_TO_FROM_VALUE[timeRangeOption];
setTimeRange([from, "now"]);
Expand All @@ -377,6 +420,8 @@ export const Metrics = () => {
const toParam = to !== null ? `&to=${to}` : "";
const timeRangeParams = `${fromParam}${toParam}`;

const refreshParams = refresh ? `&refresh=${refresh}` : "";

return (
<div>
<MainNavPageInfo
Expand Down Expand Up @@ -435,19 +480,52 @@ export const Metrics = () => {
className={classes.timeRangeButton}
select
size="small"
style={{ width: 120 }}
sx={{ width: 80 }}
value={refreshOption}
onChange={({ target: { value } }) => {
setRefreshOption(value as RefreshOptions);
}}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BiRefresh style={{ fontSize: 25, paddingBottom: 5 }} />
</InputAdornment>
),
}}
>
{Object.entries(RefreshOptions).map(([key, value]) => (
<MenuItem key={key} value={value}>
{value}
</MenuItem>
))}
</TextField>
<HelpInfo>Auto-refresh interval</HelpInfo>
<TextField
className={classes.timeRangeButton}
select
size="small"
sx={{ width: 140 }}
value={timeRangeOption}
onChange={({ target: { value } }) => {
setTimeRangeOption(value as TimeRangeOptions);
}}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BiTime style={{ fontSize: 22, paddingBottom: 5 }} />
</InputAdornment>
),
}}
>
{Object.entries(TimeRangeOptions).map(([key, value]) => (
<MenuItem key={key} value={value}>
{value}
</MenuItem>
))}
</TextField>
<HelpInfo>Time range picker</HelpInfo>
</Paper>
<Alert severity="info">
Tip: You can click on the legend to focus on a specific line in the
Expand All @@ -459,6 +537,7 @@ export const Metrics = () => {
<MetricsSection
key={config.title}
metricConfig={config}
refreshParams={refreshParams}
timeRangeParams={timeRangeParams}
dashboardUid={grafanaDefaultDashboardUid}
dashboardDatasource={grafanaDefaultDatasource}
Expand All @@ -469,6 +548,7 @@ export const Metrics = () => {
<MetricsSection
key={config.title}
metricConfig={config}
refreshParams={refreshParams}
timeRangeParams={timeRangeParams}
dashboardUid={dashboardUids["data"]}
dashboardDatasource={grafanaDefaultDatasource}
Expand Down Expand Up @@ -511,13 +591,15 @@ const useMetricsSectionStyles = makeStyles((theme) =>

type MetricsSectionProps = {
metricConfig: MetricsSectionConfig;
refreshParams: string;
timeRangeParams: string;
dashboardUid: string;
dashboardDatasource: string;
};

const MetricsSection = ({
metricConfig: { title, contents },
refreshParams,
timeRangeParams,
dashboardUid,
dashboardDatasource,
Expand All @@ -538,7 +620,7 @@ const MetricsSection = ({
{contents.map(({ title, pathParams }) => {
const path =
`/d-solo/${dashboardUid}?${pathParams}` +
`&refresh${timeRangeParams}&var-SessionName=${sessionName}&var-datasource=${dashboardDatasource}`;
`&${refreshParams}${timeRangeParams}&var-SessionName=${sessionName}&var-datasource=${dashboardDatasource}`;
return (
<Paper
key={pathParams}
Expand Down
62 changes: 59 additions & 3 deletions dashboard/client/src/pages/serve/ServeDeploymentMetricsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { Box, Button, MenuItem, Paper, TextField } from "@mui/material";
import {
Box,
Button,
InputAdornment,
MenuItem,
Paper,
TextField,
} from "@mui/material";
import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles";
import React, { useContext, useEffect, useState } from "react";
import { BiRefresh, BiTime } from "react-icons/bi";
import { RiExternalLinkLine } from "react-icons/ri";
import { GlobalContext } from "../../App";
import { CollapsibleSection } from "../../common/CollapsibleSection";
import { ClassNameProps } from "../../common/props";
import { HelpInfo } from "../../components/Tooltip";
import {
MetricConfig,
REFRESH_VALUE,
RefreshOptions,
TIME_RANGE_TO_FROM_VALUE,
TimeRangeOptions,
} from "../metrics";
Expand Down Expand Up @@ -86,13 +97,23 @@ export const ServeReplicaMetricsSection = ({
const grafanaServeDashboardUid =
dashboardUids?.serveDeployment ?? "rayServeDashboard";

const [refreshOption, setRefreshOption] = useState<RefreshOptions>(
RefreshOptions.FIVE_SECONDS,
);

const [timeRangeOption, setTimeRangeOption] = useState<TimeRangeOptions>(
TimeRangeOptions.FIVE_MINS,
);

const [refresh, setRefresh] = useState<string | null>(null);

const [[from, to], setTimeRange] = useState<[string | null, string | null]>([
null,
null,
]);
useEffect(() => {
setRefresh(REFRESH_VALUE[refreshOption]);
}, [refreshOption]);
useEffect(() => {
const from = TIME_RANGE_TO_FROM_VALUE[timeRangeOption];
setTimeRange([from, "now"]);
Expand All @@ -101,6 +122,7 @@ export const ServeReplicaMetricsSection = ({
const fromParam = from !== null ? `&from=${from}` : "";
const toParam = to !== null ? `&to=${to}` : "";
const timeRangeParams = `${fromParam}${toParam}`;
const refreshParams = refresh ? `&refresh=${refresh}` : "";

const replicaButtonUrl = useViewServeDeploymentMetricsButtonUrl(
deploymentName,
Expand All @@ -125,24 +147,58 @@ export const ServeReplicaMetricsSection = ({
className={classes.timeRangeButton}
select
size="small"
style={{ width: 120 }}
sx={{ width: 80 }}
value={refreshOption}
onChange={({ target: { value } }) => {
setRefreshOption(value as RefreshOptions);
}}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BiRefresh style={{ fontSize: 25, paddingBottom: 5 }} />
</InputAdornment>
),
}}
>
{Object.entries(RefreshOptions).map(([key, value]) => (
<MenuItem key={key} value={value}>
{value}
</MenuItem>
))}
</TextField>
<HelpInfo>Auto-refresh interval</HelpInfo>
<TextField
className={classes.timeRangeButton}
select
size="small"
style={{ width: 140 }}
value={timeRangeOption}
onChange={({ target: { value } }) => {
setTimeRangeOption(value as TimeRangeOptions);
}}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BiTime style={{ fontSize: 22, paddingBottom: 5 }} />
</InputAdornment>
),
}}
>
{Object.entries(TimeRangeOptions).map(([key, value]) => (
<MenuItem key={key} value={value}>
{value}
</MenuItem>
))}
</TextField>
<HelpInfo>Time range picker</HelpInfo>
</Box>
<div className={classes.grafanaEmbedsContainer}>
{METRICS_CONFIG.map(({ title, pathParams }) => {
const path =
`/d-solo/${grafanaServeDashboardUid}?${pathParams}` +
`&refresh${timeRangeParams}&var-Deployment=${encodeURIComponent(
`${refreshParams}${timeRangeParams}&var-Deployment=${encodeURIComponent(
deploymentName,
)}&var-Replica=${encodeURIComponent(
replicaId,
Expand Down
Loading
Loading