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

prometheus: Update look and add interval selector #140

Merged
merged 12 commits into from
Jan 16, 2025
9 changes: 5 additions & 4 deletions prometheus/src/components/Chart/CPUChart/CPUChart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useTheme } from '@mui/material';
import { alpha, useTheme } from '@mui/material';
import { blue } from '@mui/material/colors';
import { fetchMetrics } from '../../../request';
import { createTickTimestampFormatter, dataProcessor } from '../../../util';
import Chart from '../Chart/Chart';
Expand Down Expand Up @@ -45,7 +46,7 @@ export function CPUChart(props: CPUChartProps) {

const YTickProps = {
domain: ['dataMin', 'auto'],
width: 80,
width: 60,
};

return (
Expand All @@ -54,8 +55,8 @@ export function CPUChart(props: CPUChartProps) {
{
query: props.query,
name: 'cpu (cores)',
strokeColor: '#CDC300',
fillColor: '#FFF178',
strokeColor: alpha(blue[400], 0.8),
fillColor: alpha(blue[400], 0.1),
dataProcessor: dataProcessor,
},
]}
Expand Down
35 changes: 27 additions & 8 deletions prometheus/src/components/Chart/Chart/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { EmptyContent, Loader } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { Box, useTheme } from '@mui/material';
import { useEffect, useState } from 'react';
import { Area, AreaChart, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import {
Area,
AreaChart,
CartesianGrid,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { getTimeRange } from '../../../util';

/**
Expand Down Expand Up @@ -50,10 +59,11 @@ export default function Chart(props: ChartProps) {
SUCCESS,
}
const { fetchMetrics, xAxisProps, yAxisProps } = props;
const [metrics, setMetrics] = useState<object>({});
const [metrics, setMetrics] = useState<Array<any>>([]);
const [state, setState] = useState<ChartState | null>(null);
const [error, setError] = useState<string | null>(null);
const theme = useTheme();
const timeRange = getTimeRange(props.interval);

const fetchMetricsData = async (
plots: Array<{ query: string; name: string; dataProcessor: (data: any) => any }>,
Expand Down Expand Up @@ -142,29 +152,38 @@ export default function Chart(props: ChartProps) {
clearInterval(refreshInterval);
};
}
}, [props.autoRefresh, props.plots]);
}, [props.autoRefresh, props.plots, props.interval]);

let chartContent;

if (state === ChartState.SUCCESS) {
chartContent = (
<AreaChart data={metrics}>
<XAxis stroke={theme.palette.chartStyles.labelColor} {...xAxisProps} />
<YAxis stroke={theme.palette.chartStyles.labelColor} {...yAxisProps} />
<AreaChart data={metrics} style={{ fontSize: 14 }}>
<XAxis
stroke={theme.palette.chartStyles.labelColor}
fontSize={12}
{...xAxisProps}
type="number"
domain={[timeRange.from, timeRange.to]}
allowDataOverflow
/>
<YAxis fontSize={14} stroke={theme.palette.chartStyles.labelColor} {...yAxisProps} />
{props.CustomTooltip === undefined ? (
<Tooltip />
) : (
<Tooltip content={props.CustomTooltip} />
)}
<Legend />
<CartesianGrid strokeDasharray="2 4" stroke={theme.palette.divider} vertical={false} />
{props.plots.map(plot => (
<Area
stackId="1"
type="monotone"
type="step"
dataKey={plot.name}
stroke={plot.strokeColor}
strokeWidth={2}
fill={plot.fillColor}
activeDot={{ r: 8 }}
activeDot={{ r: 2 }}
animationDuration={props.autoRefresh ? 0 : 400} // Disable animation when refreshing
/>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useTheme } from '@mui/material';
import { alpha, useTheme } from '@mui/material';
import { orange, purple } from '@mui/material/colors';
import { fetchMetrics } from '../../../request';
import { createTickTimestampFormatter, dataProcessor } from '../../../util';
import { formatBytes } from '../../../util';
Expand Down Expand Up @@ -32,15 +33,15 @@ export function FilesystemChart(props: FilesystemChartProps) {
{
query: props.readQuery,
name: 'read',
strokeColor: '#CDC300',
fillColor: '#FFF178',
strokeColor: alpha(orange[400], 0.8),
fillColor: alpha(orange[400], 0.1),
dataProcessor: dataProcessor,
},
{
query: props.writeQuery,
name: 'write',
strokeColor: '#006B58',
fillColor: '#98F6DC',
strokeColor: alpha(purple[400], 0.8),
fillColor: alpha(purple[400], 0.1),
dataProcessor: dataProcessor,
},
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { Icon } from '@iconify/react';
import { Loader } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { SectionBox } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { useCluster } from '@kinvolk/headlamp-plugin/lib/k8s';
import { Box, IconButton, ToggleButton, ToggleButtonGroup, Tooltip } from '@mui/material';
import {
Box,
Button,
MenuItem,
Paper,
Select,
ToggleButton,
ToggleButtonGroup,
} from '@mui/material';
import Alert from '@mui/material/Alert';
import { useEffect, useState } from 'react';
import { getConfigStore, getPrometheusInterval, getPrometheusPrefix } from '../../../util';
Expand Down Expand Up @@ -82,114 +90,142 @@ export function GenericMetricsChart(props: GenericMetricsChartProps) {
setChartVariant(event.currentTarget.value);
};

const interval = getPrometheusInterval(cluster);

const [timespan, setTimespan] = useState(interval ?? '1h');

if (!isVisible) {
return null;
}

const interval = getPrometheusInterval(cluster);

return (
<SectionBox>
<Box
display="flex"
justifyContent="space-around"
alignItems="center"
style={{ marginBottom: '1rem', margin: '0 auto', width: '0' }}
>
{state === prometheusState.INSTALLED
? [
<ToggleButtonGroup
onChange={handleChartVariantChange}
<Paper variant="outlined" sx={{ p: 1 }}>
{state === prometheusState.INSTALLED && (
<Box display="flex" gap={1} justifyContent="flex-end" mb={2}>
<Button
variant="outlined"
size="small"
onClick={() => {
setRefresh(refresh => !refresh);
}}
startIcon={<Icon icon={refresh ? 'mdi:pause' : 'mdi:play'} />}
sx={{ filter: 'grayscale(1.0)' }}
>
{refresh ? 'Pause' : 'Resume'}
</Button>
<ToggleButtonGroup
onChange={handleChartVariantChange}
size="small"
aria-label="metric chooser"
value={chartVariant}
exclusive
>
<CustomToggleButton label="CPU" value="cpu" icon="mdi:chip" />
<CustomToggleButton label="Memory" value="memory" icon="mdi:memory" />
<CustomToggleButton
label="Network"
value="network"
icon="mdi:folder-network-outline"
/>
<CustomToggleButton label="Filesystem" value="filesystem" icon="mdi:database" />
</ToggleButtonGroup>
<Box>
<Select
variant="outlined"
size="small"
aria-label="metric chooser"
value={chartVariant}
exclusive
name="Time"
value={timespan}
onChange={e => setTimespan(e.target.value)}
>
<ToggleButton value="cpu">CPU</ToggleButton>
<ToggleButton value="memory">Memory</ToggleButton>
<ToggleButton value="network">Network</ToggleButton>
<ToggleButton value="filesystem">Filesystem</ToggleButton>
</ToggleButtonGroup>,
<Box pl={2}>
<IconButton
onClick={() => {
setRefresh(refresh => !refresh);
}}
size="large"
>
{refresh ? (
<Tooltip title="Pause metrics">
{' '}
<Icon icon="mdi:pause" />{' '}
</Tooltip>
) : (
<Tooltip title="Resume metrics">
{' '}
<Icon icon="mdi:play" />{' '}
</Tooltip>
)}
</IconButton>
</Box>,
]
: []}
</Box>

{state === prometheusState.INSTALLED ? (
<Box
style={{
justifyContent: 'center',
display: 'flex',
height: '40vh',
width: '80%',
margin: '0 auto',
}}
>
{chartVariant === 'cpu' && (
<CPUChart
query={props.cpuQuery}
autoRefresh={refresh}
prometheusPrefix={prometheusPrefix}
interval={interval}
/>
)}
{chartVariant === 'memory' && (
<MemoryChart
query={props.memoryQuery}
autoRefresh={refresh}
prometheusPrefix={prometheusPrefix}
interval={interval}
/>
)}
{chartVariant === 'network' && (
<NetworkChart
rxQuery={props.networkRxQuery}
txQuery={props.networkTxQuery}
autoRefresh={refresh}
interval={interval}
prometheusPrefix={prometheusPrefix}
/>
)}
{chartVariant === 'filesystem' && (
<FilesystemChart
readQuery={props.filesystemReadQuery}
writeQuery={props.filesystemWriteQuery}
autoRefresh={refresh}
interval={interval}
prometheusPrefix={prometheusPrefix}
/>
)}
</Box>
) : state === prometheusState.LOADING ? (
<Box m={2}>
<Loader title="Loading Prometheus Info" />
</Box>
) : state === prometheusState.ERROR ? (
<Box m={2}>
<Alert severity="warning">Error fetching prometheus Info</Alert>
</Box>
) : (
<PrometheusNotFoundBanner />
)}
<MenuItem value={'10m'}>10 minutes</MenuItem>
<MenuItem value={'30m'}>30 minutes</MenuItem>
<MenuItem value={'1h'}>1 hour</MenuItem>
<MenuItem value={'3h'}>3 hours</MenuItem>
<MenuItem value={'6h'}>6 hours</MenuItem>
<MenuItem value={'12h'}>12 hours</MenuItem>
<MenuItem value={'24h'}>24 hours</MenuItem>
<MenuItem value={'48h'}>48 hours</MenuItem>
<MenuItem value={'today'}>Today</MenuItem>
<MenuItem value={'yesterday'}>Yesterday</MenuItem>
<MenuItem value={'week'}>Week</MenuItem>
<MenuItem value={'lastweek'}>Last week</MenuItem>
<MenuItem value={'7d'}>7 days</MenuItem>
<MenuItem value={'14d'}>14 days</MenuItem>
</Select>
</Box>
</Box>
)}
{state === prometheusState.INSTALLED ? (
<Box
style={{
height: '400px',
}}
>
{chartVariant === 'cpu' && (
<CPUChart
query={props.cpuQuery}
autoRefresh={refresh}
prometheusPrefix={prometheusPrefix}
interval={timespan}
/>
)}
{chartVariant === 'memory' && (
<MemoryChart
query={props.memoryQuery}
autoRefresh={refresh}
prometheusPrefix={prometheusPrefix}
interval={timespan}
/>
)}
{chartVariant === 'network' && (
<NetworkChart
rxQuery={props.networkRxQuery}
txQuery={props.networkTxQuery}
autoRefresh={refresh}
interval={timespan}
prometheusPrefix={prometheusPrefix}
/>
)}
{chartVariant === 'filesystem' && (
<FilesystemChart
readQuery={props.filesystemReadQuery}
writeQuery={props.filesystemWriteQuery}
autoRefresh={refresh}
interval={timespan}
prometheusPrefix={prometheusPrefix}
/>
)}
</Box>
) : state === prometheusState.LOADING ? (
<Box m={2}>
<Loader title="Loading Prometheus Info" />
</Box>
) : state === prometheusState.ERROR ? (
<Box m={2}>
<Alert severity="warning">Error fetching prometheus Info</Alert>
</Box>
) : (
<PrometheusNotFoundBanner />
)}
</Paper>
</SectionBox>
);
}

function CustomToggleButton({
label,
icon,
value,
}: {
label: string;
icon: string;
value: string;
}) {
return (
<ToggleButton size="small" value={value} sx={{ textTransform: 'none', gap: 0.5, fontSize: 14 }}>
<Icon icon={icon} width="18px" />
{label}
</ToggleButton>
);
}
Loading
Loading