1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-28 17:55:15 +02:00
unleash.unleash/frontend/src/component/admin/traffic/TrafficMetricsChart.tsx
Nuno Góis 90f0d665f9
fix: disable networkView for dev, fail more gracefully (#2701)
Disables networkView for dev for now. Attempts to fail more gracefully
both on the service and front-end.
2022-12-15 10:12:02 +00:00

220 lines
6.6 KiB
TypeScript

import {
InstanceMetrics,
useInstanceMetrics,
} from '../../../hooks/api/getters/useInstanceMetrics/useInstanceMetrics';
import { useMemo, VFC } from 'react';
import { Line } from 'react-chartjs-2';
import {
CategoryScale,
Chart as ChartJS,
ChartData,
ChartDataset,
ChartOptions,
Legend,
LinearScale,
LineElement,
PointElement,
TimeScale,
Title,
Tooltip,
} from 'chart.js';
import {
ILocationSettings,
useLocationSettings,
} from '../../../hooks/useLocationSettings';
import theme from '../../../themes/theme';
import { formatDateHM } from '../../../utils/formatDate';
import { RequestsPerSecondSchema } from 'openapi';
import 'chartjs-adapter-date-fns';
import { Alert, PaletteColor } from '@mui/material';
import { PageContent } from '../../common/PageContent/PageContent';
import { PageHeader } from '../../common/PageHeader/PageHeader';
import { Box } from '@mui/system';
import { current } from 'immer';
import { CyclicIterator } from 'utils/cyclicIterator';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
interface IPoint {
x: number;
y: number;
}
type ChartDatasetType = ChartDataset<'line', IPoint[]>;
type ChartDataType = ChartData<'line', IPoint[], string>;
const createChartPoints = (
values: Array<Array<number | string>>,
y: (m: string) => number
): IPoint[] => {
return values.map(row => ({
x: row[0] as number,
y: y(row[1] as string),
}));
};
const createInstanceChartOptions = (
metrics: InstanceMetrics,
locationSettings: ILocationSettings
): ChartOptions<'line'> => {
return {
locale: locationSettings.locale,
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
tooltip: {
backgroundColor: 'white',
bodyColor: theme.palette.text.primary,
titleColor: theme.palette.grey[700],
borderColor: theme.palette.primary.main,
borderWidth: 1,
padding: 10,
boxPadding: 5,
usePointStyle: true,
callbacks: {
title: items =>
formatDateHM(
1000 * items[0].parsed.x,
locationSettings.locale
),
},
itemSort: (a, b) => b.parsed.y - a.parsed.y,
},
legend: {
position: 'top',
align: 'end',
labels: {
boxWidth: 10,
boxHeight: 10,
usePointStyle: true,
},
},
title: {
text: 'Requests per second in the last 6 hours',
position: 'top',
align: 'start',
display: true,
font: {
size: 16,
weight: '400',
},
},
},
scales: {
y: {
type: 'linear',
title: {
display: true,
text: 'Requests per second',
},
// min: 0,
suggestedMin: 0,
ticks: { precision: 0 },
},
x: {
type: 'time',
time: { unit: 'minute' },
grid: { display: false },
ticks: {
callback: (_, i, data) =>
formatDateHM(data[i].value, locationSettings.locale),
},
},
},
};
};
const toChartData = (
rps: RequestsPerSecondSchema,
color: PaletteColor,
label: (name: string) => string
): ChartDatasetType[] => {
if (rps.data?.result) {
return rps.data.result.map(dataset => ({
label: label(dataset.metric?.appName || 'unknown'),
borderColor: color.main,
backgroundColor: color.main,
data: createChartPoints(dataset.values || [], y => parseFloat(y)),
elements: {
point: {
radius: 4,
pointStyle: 'circle',
},
line: {
borderDash: [8, 4],
},
},
}));
}
return [];
};
const createInstanceChartData = (metrics?: InstanceMetrics): ChartDataType => {
if (metrics) {
const colors = new CyclicIterator<PaletteColor>([
theme.palette.success,
theme.palette.error,
theme.palette.primary,
]);
let datasets: ChartDatasetType[] = [];
for (let key in metrics) {
datasets = datasets.concat(
toChartData(
metrics[key],
colors.next(),
metricName => `${metricName}: ${key}`
)
);
}
return { datasets };
}
return { datasets: [] };
};
export const InstanceMetricsChart: VFC = () => {
const { locationSettings } = useLocationSettings();
const { metrics } = useInstanceMetrics();
const options = useMemo(() => {
return createInstanceChartOptions(metrics, locationSettings);
}, [metrics, locationSettings]);
const data = useMemo(() => {
return createInstanceChartData(metrics);
}, [metrics, locationSettings]);
return (
<PageContent header={<PageHeader title="Requests per second" />}>
<ConditionallyRender
condition={data.datasets.length === 0}
show={<Alert severity="warning">No data available.</Alert>}
elseShow={
<Box sx={{ display: 'grid', gap: 4 }}>
<div style={{ height: 400 }}>
<Line
data={data}
options={options}
aria-label="An instance metrics line chart with two lines: requests per second for admin API and requests per second for client API"
/>
</div>
</Box>
}
/>
</PageContent>
);
};
// Register dependencies that we need to draw the chart.
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
TimeScale,
Legend,
Tooltip,
Title
);
// Use a default export to lazy-load the charting library.
export default InstanceMetricsChart;