Files
go-service/cmd/server/web/logs.html
2026-06-17 09:53:29 +03:00

373 lines
9.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Журнал логов</title>
<link rel="icon" type="image/png" href="/favicon.png">
<link rel="stylesheet" href="/css/style.css">
<style>
/* Дополнительные стили для журнала */
.logs-container {
display: flex;
gap: 20px;
height: calc(100vh - 120px);
}
.logs-sidebar {
flex: 0 0 280px;
border-right: 1px solid #00ff88;
padding-right: 16px;
overflow-y: auto;
}
.logs-content {
flex: 1;
overflow-y: auto;
}
.log-file-item {
padding: 8px 12px;
margin: 4px 0;
cursor: pointer;
border-radius: 4px;
border: 1px solid transparent;
transition: all 0.2s;
}
.log-file-item:hover {
background: rgba(0,255,136,0.1);
border-color: #00ff88;
}
.log-file-item.active {
background: rgba(0,255,136,0.2);
border-color: #00ff88;
}
.log-file-item .file-name {
font-size: 13px;
}
.log-file-item .file-info {
font-size: 11px;
opacity: 0.6;
}
.log-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.log-stat-card {
padding: 12px;
border: 1px solid #00ff88;
border-radius: 4px;
text-align: center;
}
.log-stat-card .value {
font-size: 24px;
font-weight: bold;
}
.log-stat-card .label {
font-size: 11px;
opacity: 0.6;
}
.data-table {
width: 100%;
font-size: 12px;
border-collapse: collapse;
}
.data-table th {
text-align: left;
padding: 6px 8px;
border-bottom: 1px solid #00ff88;
position: sticky;
top: 0;
background: #050805;
}
.data-table td {
padding: 4px 8px;
border-bottom: 1px solid rgba(0,255,136,0.1);
}
.data-table tr:hover {
background: rgba(0,255,136,0.05);
}
.strength-0 { color: #666; }
.strength-1 { color: #88ff88; }
.strength-2 { color: #ffaa44; }
.strength-3 { color: #ff4444; }
.events-list {
font-family: monospace;
font-size: 13px;
line-height: 1.6;
}
.events-list .event-time {
opacity: 0.5;
}
.tabs {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
.tab-btn {
padding: 6px 16px;
background: transparent;
color: #00ff88;
border: 1px solid #00ff88;
border-radius: 4px;
cursor: pointer;
font-family: monospace;
}
.tab-btn.active {
background: rgba(0,255,136,0.2);
}
.tab-btn:hover {
background: rgba(0,255,136,0.1);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
</style>
</head>
<body>
<div class="header-bar">
<h1>📋 ЖУРНАЛ ЛОГОВ</h1>
<div id="server-time" class="header-time">--:--:--</div>
<a href="/" style="color:#00ff88; text-decoration:none; font-size:14px;">На главную</a>
</div>
<div class="logs-container">
<div class="logs-sidebar">
<h3 style="margin-top:0;">📁 Файлы данных</h3>
<div id="fileList"></div>
</div>
<div class="logs-content">
<div class="tabs">
<button class="tab-btn active" data-tab="data">📊 Данные</button>
<button class="tab-btn" data-tab="stats">📈 Статистика</button>
<button class="tab-btn" data-tab="events">📋 События</button>
</div>
<div id="tab-data" class="tab-content active">
<div id="logStats" class="log-stats"></div>
<div id="chartContainer" style="margin-bottom:16px;">
<canvas id="logChart" height="200"></canvas>
</div>
<div id="dataTableContainer" style="max-height:400px; overflow-y:auto;"></div>
</div>
<div id="tab-stats" class="tab-content">
<div id="statsContent">Выберите файл для просмотра статистики</div>
</div>
<div id="tab-events" class="tab-content">
<div id="eventsContent" class="events-list">Загрузка...</div>
</div>
</div>
</div>
<script type="module">
import { drawChart } from '/dist/chart.js';
// Состояние
let currentFile = null;
let currentData = null;
let currentEvents = [];
// DOM элементы
const fileList = document.getElementById('fileList');
const logStats = document.getElementById('logStats');
const dataTableContainer = document.getElementById('dataTableContainer');
const logChart = document.getElementById('logChart');
const eventsContent = document.getElementById('eventsContent');
// Загрузка списка файлов
async function loadFileList() {
try {
const response = await fetch('/api/log/files');
const files = await response.json();
fileList.innerHTML = '';
files.forEach(file => {
const div = document.createElement('div');
div.className = 'log-file-item';
if (currentFile === file.name) div.classList.add('active');
div.innerHTML = `
<div class="file-name">${file.name}</div>
<div class="file-info">${file.time} | ${(file.size/1024).toFixed(1)} KB</div>
`;
div.addEventListener('click', () => loadFileData(file.name));
fileList.appendChild(div);
});
// Автоматически загружаем первый файл
if (files.length > 0 && !currentFile) {
loadFileData(files[0].name);
}
} catch (error) {
console.error('Error loading file list:', error);
}
}
// Загрузка данных файла
async function loadFileData(filename) {
currentFile = filename;
// Обновляем активный элемент в списке
document.querySelectorAll('.log-file-item').forEach(el => el.classList.remove('active'));
const items = document.querySelectorAll('.log-file-item');
for (const item of items) {
if (item.textContent.includes(filename)) {
item.classList.add('active');
break;
}
}
try {
const response = await fetch(`/api/log/data?file=${encodeURIComponent(filename)}`);
const data = await response.json();
currentData = data;
renderData(data);
} catch (error) {
console.error('Error loading data:', error);
}
}
// Отрисовка данных
function renderData(data) {
const samples = data.samples || [];
// Статистика
if (samples.length > 0) {
const values = samples.map(s => s.value);
const counts = samples.map(s => s.count);
const max = Math.max(...values);
const min = Math.min(...values);
const avg = values.reduce((a,b) => a + b, 0) / values.length;
const totalObjects = counts.reduce((a,b) => a + b, 0);
logStats.innerHTML = `
<div class="log-stat-card">
<div class="value">${samples.length}</div>
<div class="label">Всего точек</div>
</div>
<div class="log-stat-card">
<div class="value">${max}</div>
<div class="label">Максимум</div>
</div>
<div class="log-stat-card">
<div class="value">${avg.toFixed(1)}</div>
<div class="label">Среднее</div>
</div>
<div class="log-stat-card">
<div class="value">${totalObjects}</div>
<div class="label">Объектов всего</div>
</div>
`;
}
// График
if (logChart && samples.length > 0) {
const history = samples.map(s => s.count);
drawChart(logChart, history);
}
// Таблица — только Время, Объектов, Сила
if (samples.length > 0) {
let html = `<table class="data-table">
<thead>
<tr>
<th>Время</th>
<th>Объектов</th>
<th>Сила</th>
</tr>
</thead>
<tbody>`;
const displaySamples = samples.slice(-200);
displaySamples.forEach(s => {
const strengthClass = `strength-${s.strength}`;
html += `<tr>
<td>${s.time}</td>
<td>${s.count}</td>
<td class="${strengthClass}">${s.strength_name}</td>
</tr>`;
});
html += '</tbody></table>';
dataTableContainer.innerHTML = html;
} else {
dataTableContainer.innerHTML = '<p>Нет данных</p>';
}
}
// Загрузка событий
async function loadEvents() {
try {
const response = await fetch('/api/log/events?limit=200');
const data = await response.json();
currentEvents = data.events || [];
let html = '';
currentEvents.forEach(e => {
html += `<div><span class="event-time">[${e.time}]</span> ${e.event}</div>`;
});
eventsContent.innerHTML = html || 'Нет событий';
} catch (error) {
eventsContent.innerHTML = 'Ошибка загрузки событий';
}
}
// Переключение табов
document.querySelectorAll('.tab-btn[data-tab]').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tab-btn[data-tab]').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const tabName = btn.dataset.tab;
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
document.getElementById(`tab-${tabName}`).classList.add('active');
if (tabName === 'events') {
loadEvents();
}
});
});
// Инициализация
loadFileList();
// Обновление времени
function updateTime() {
const now = new Date();
document.getElementById('server-time').textContent = now.toLocaleTimeString();
}
setInterval(updateTime, 1000);
updateTime();
</script>
</body>
</html>