trust-module-frontend/src/Charts/PrometheusChart.jsx

197 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useEffect, useState, useRef } from 'react';
import axios from 'axios';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import LineChartComponent from './Components/LineChartComponent';
const MAX_POINTS = 20; // Ограничение точек на графике
const COLORS = ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850']; // Фиксированные цвета для линий
// Список временных диапазонов и интервалов обновления
const TIME_RANGES = [
{ label: '1 минута', value: 60, interval: 3000 },
{ label: '5 минут', value: 300, interval: 15000 },
{ label: '30 минут', value: 1800, interval: 90000 },
{ label: '1 час', value: 3600, interval: 180000 },
{ label: '3 часа', value: 10800, interval: 540000 },
{ label: '6 часов', value: 21600, interval: 1080000 },
{ label: '12 часов', value: 43200, interval: 2160000 },
{ label: '24 часа', value: 86400, interval: 4320000 },
{ label: '2 дня', value: 172800, interval: 8640000 },
{ label: '7 дней', value: 604800, interval: 30240000 },
{ label: '30 дней', value: 2592000, interval: 129600000 },
{ label: '90 дней', value: 7776000, interval: 388800000 },
{ label: '6 месяцев', value: 15552000, interval: 777600000 },
{ label: '9 месяцев', value: 23328000, interval: 1166400000 },
{ label: '1 год', value: 31536000, interval: 1576800000 },
];
const PrometheusChart = ({ metricName }) => {
const [chartData, setChartData] = useState({});
const [metricType, setMetricType] = useState('');
const [metricDescription, setMetricDescription] = useState('');
const [selectedRange, setSelectedRange] = useState(TIME_RANGES[0]); // По умолчанию 1 минута
const [startDate, setStartDate] = useState(new Date()); // Начальная дата для кастомного диапазона
const [endDate, setEndDate] = useState(new Date()); // Конечная дата для кастомного диапазона
const [useCustomRange, setUseCustomRange] = useState(false); // Флаг для выбора кастомного диапазона
const [brushRange, setBrushRange] = useState({ startIndex: 0, endIndex: 0 }); // Состояние Brush
const intervalRef = useRef(null);
const fetchData = async () => {
try {
let start, end;
if (useCustomRange) {
// Используем кастомный диапазон
start = Math.floor(startDate.getTime() / 1000);
end = Math.floor(endDate.getTime() / 1000);
} else {
// Используем предустановленный диапазон
end = Math.floor(Date.now() / 1000);
start = end - selectedRange.value;
}
// Динамический шаг (чем больше диапазон, тем больше шаг)
let step;
const range = end - start;
if (range <= 3600) step = 5; // 1 час и меньше → 5 сек
else if (range <= 21600) step = 30; // 1-6 часов → 30 сек
else if (range <= 86400) step = 120; // 6-24 часа → 2 минуты
else step = 300; // > 24 часов → 5 минут
console.log(`Запрашиваем данные с шагом ${step} сек`);
const response = await axios.get('http://192.168.2.39:3000/metrics', {
params: { metric: metricName, start, end, step },
});
const result = response.data;
let metrics = Array.isArray(result) ? result : result.data || [];
if (!Array.isArray(metrics) || metrics.length === 0) {
console.warn('No metrics data available, filling with empty values.');
metrics = [];
}
// 1. Генерация временных точек с учетом диапазона
const timePoints = [];
for (let t = start; t <= end; t += step) {
const date = new Date(t * 1000);
const formattedTime = range > 86400
? date.toLocaleString([], { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })
: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
timePoints.push(formattedTime);
}
// 2. Обработка данных
const updatedData = {};
metrics.forEach(m => {
const date = new Date(m.timestamp);
const formattedTime = range > 86400
? date.toLocaleString([], { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })
: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
const key = `${m.instance}-${m.device || m.scrape_job}`;
if (!updatedData[key]) updatedData[key] = {};
updatedData[key][formattedTime] = m.value;
});
// 3. Заполнение пропусков
const chartData = {};
Object.keys(updatedData).forEach(key => {
chartData[key] = timePoints.map(time => ({
time,
value: updatedData[key][time] ?? null,
}));
});
setChartData(chartData);
// Устанавливаем Brush на весь диапазон
setBrushRange({
startIndex: 0,
endIndex: timePoints.length - 1,
});
} catch (error) {
console.error('Ошибка при загрузке метрик:', error);
}
};
useEffect(() => {
fetchData(); // Первоначальная загрузка данных
intervalRef.current = setInterval(() => {
fetchData();
}, selectedRange.interval); // Обновляем с выбранным интервалом
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current); // Очищаем интервал при размонтировании
}
};
}, [metricName, selectedRange, useCustomRange, startDate, endDate]); // Зависимость от metricName, selectedRange, useCustomRange, startDate и endDate
const handleRangeChange = (event) => {
const selectedValue = event.target.value;
const range = TIME_RANGES.find(range => range.value === parseInt(selectedValue, 10));
setSelectedRange(range);
setUseCustomRange(false); // Переключаемся на предустановленный диапазон
};
const handleCustomRangeChange = () => {
setUseCustomRange(true); // Переключаемся на кастомный диапазон
};
if (!Object.keys(chartData).length) return <p>Loading...</p>;
return (
<div>
<div>
<label htmlFor="time-range">Выберите временной диапазон: </label>
<select id="time-range" value={selectedRange.value} onChange={handleRangeChange}>
{TIME_RANGES.map(range => (
<option key={range.value} value={range.value}>{range.label}</option>
))}
</select>
</div>
<div>
<label>Или выберите другой диапазон: </label>
<div>
<label>Начальная дата: </label>
<DatePicker
selected={startDate}
onChange={(date) => setStartDate(date)}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={15}
dateFormat="yyyy-MM-dd HH:mm"
/>
</div>
<div>
<label>Конечная дата: </label>
<DatePicker
selected={endDate}
onChange={(date) => setEndDate(date)}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={15}
dateFormat="yyyy-MM-dd HH:mm"
/>
</div>
<button onClick={handleCustomRangeChange}>Использовать кастомный диапазон</button>
</div>
<LineChartComponent
chartData={chartData}
metricName={metricName}
metricType={metricType}
colors={COLORS}
description={metricDescription}
brushRange={brushRange}
onBrushChange={setBrushRange}
/>
</div>
);
};
export default PrometheusChart;