Files
aitbc/website/dashboards/metrics.html
aitbc 40ddf89b9c
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Waiting to run
Documentation Validation / validate-docs (push) Waiting to run
CLI Tests / test-cli (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
docs: update CLI command syntax across workflow documentation
- Updated marketplace commands: `marketplace --action` → `market` subcommands
- Updated wallet commands: direct flags → `wallet` subcommands
- Updated AI commands: `ai-submit`, `ai-status` → `ai submit`, `ai status`
- Updated blockchain commands: `chain` → `blockchain info`
- Standardized command structure across all workflow files
- Affected files: MULTI_NODE_MASTER_INDEX.md, TEST_MASTER_INDEX.md, multi-node-blockchain-marketplace
2026-04-08 12:10:21 +02:00

313 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Metrics Dashboard</title>
<link rel="stylesheet" href="/assets/css/site-header.css">
<link rel="stylesheet" href="/assets/css/dashboards.css">
<link rel="preload" href="/assets/css/font-awesome.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
<body>
<div data-global-header></div>
<main>
<div class="container">
<!-- Header -->
<div class="dashboard-header">
<h1><i class="fas fa-chart-line"></i> System Metrics Dashboard</h1>
<p>Real-time monitoring of AITBC system performance and health</p>
<div class="last-updated">
<span>Last Updated: <span id="last-updated">Loading...</span></span>
<button onclick="refreshMetrics()" class="btn btn-primary">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
</div>
<!-- Metrics Grid -->
<div class="metrics-grid">
<!-- API Metrics -->
<div class="metric-card">
<div class="metric-header">
<h3><i class="fas fa-server"></i> API Metrics</h3>
</div>
<div class="metric-body">
<div class="metric-item">
<span class="metric-label">Total Requests</span>
<span class="metric-value" id="api-requests">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Errors</span>
<span class="metric-value error" id="api-errors">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Error Rate</span>
<span class="metric-value" id="error-rate">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Avg Response Time</span>
<span class="metric-value" id="avg-response-time">-</span>
</div>
</div>
</div>
<!-- Database Metrics -->
<div class="metric-card">
<div class="metric-header">
<h3><i class="fas fa-database"></i> Database Metrics</h3>
</div>
<div class="metric-body">
<div class="metric-item">
<span class="metric-label">Queries</span>
<span class="metric-value" id="db-queries">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Errors</span>
<span class="metric-value error" id="db-errors">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Active Connections</span>
<span class="metric-value" id="active-connections">-</span>
</div>
</div>
</div>
<!-- Cache Metrics -->
<div class="metric-card">
<div class="metric-header">
<h3><i class="fas fa-memory"></i> Cache Metrics</h3>
</div>
<div class="metric-body">
<div class="metric-item">
<span class="metric-label">Cache Hits</span>
<span class="metric-value success" id="cache-hits">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Cache Misses</span>
<span class="metric-value warning" id="cache-misses">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Hit Rate</span>
<span class="metric-value" id="cache-hit-rate">-</span>
</div>
</div>
</div>
<!-- System Metrics -->
<div class="metric-card">
<div class="metric-header">
<h3><i class="fas fa-microchip"></i> System Metrics</h3>
</div>
<div class="metric-body">
<div class="metric-item">
<span class="metric-label">Memory Usage</span>
<span class="metric-value" id="memory-usage">-</span>
</div>
<div class="metric-item">
<span class="metric-label">CPU Usage</span>
<span class="metric-value" id="cpu-usage">-</span>
</div>
<div class="metric-item">
<span class="metric-label">Uptime</span>
<span class="metric-value" id="uptime">-</span>
</div>
</div>
</div>
</div>
<!-- Status Indicators -->
<div class="status-section">
<h2>System Status</h2>
<div class="status-indicators">
<div class="status-item" id="status-api">
<span class="status-label">API Service</span>
<span class="status-badge">Checking...</span>
</div>
<div class="status-item" id="status-database">
<span class="status-label">Database</span>
<span class="status-badge">Checking...</span>
</div>
<div class="status-item" id="status-cache">
<span class="status-label">Cache</span>
<span class="status-badge">Checking...</span>
</div>
<div class="status-item" id="status-blockchain">
<span class="status-label">Blockchain</span>
<span class="status-badge">Checking...</span>
</div>
</div>
</div>
<div class="status-section">
<h2>Active Alerts</h2>
<div class="status-indicators" id="alert-indicators">
<div class="status-item">
<span class="status-label">Alert State</span>
<span class="status-badge">Loading...</span>
</div>
</div>
</div>
</div>
</main>
<script>
// Auto-refresh metrics every 30 seconds
let refreshInterval;
const METRICS_ENDPOINT = '/v1/metrics';
const HEALTH_ENDPOINT = '/v1/health';
function refreshMetrics() {
// Update timestamp
document.getElementById('last-updated').textContent = new Date().toLocaleTimeString();
fetchMetrics();
checkServiceStatus();
}
async function fetchMetrics() {
try {
const response = await fetch(METRICS_ENDPOINT, { method: 'GET', cache: 'no-cache' });
if (!response.ok) {
throw new Error(`Metrics request failed with status ${response.status}`);
}
const metrics = await response.json();
// Update UI
document.getElementById('api-requests').textContent = metrics.api_requests.toLocaleString();
document.getElementById('api-errors').textContent = metrics.api_errors.toLocaleString();
document.getElementById('error-rate').textContent = Number(metrics.error_rate_percent).toFixed(2) + '%';
document.getElementById('avg-response-time').textContent = Number(metrics.avg_response_time_ms).toFixed(2) + 'ms';
document.getElementById('db-queries').textContent = metrics.database_queries.toLocaleString();
document.getElementById('db-errors').textContent = metrics.database_errors.toLocaleString();
document.getElementById('active-connections').textContent = metrics.active_connections;
document.getElementById('cache-hits').textContent = metrics.cache_hits.toLocaleString();
document.getElementById('cache-misses').textContent = metrics.cache_misses.toLocaleString();
document.getElementById('cache-hit-rate').textContent = Number(metrics.cache_hit_rate_percent).toFixed(2) + '%';
document.getElementById('memory-usage').textContent = Number(metrics.memory_usage_mb).toFixed(2) + ' MB';
document.getElementById('cpu-usage').textContent = Number(metrics.cpu_usage_percent).toFixed(2) + '%';
document.getElementById('uptime').textContent = metrics.uptime_formatted;
resetMetricClasses();
setMetricColor('error-rate', metrics.error_rate_percent, 1, 5);
setMetricColor('avg-response-time', metrics.avg_response_time_ms, 500, 1000);
setMetricColor('cache-hit-rate', metrics.cache_hit_rate_percent, 70, 85, true);
setMetricColor('memory-usage', (metrics.alerts?.memory_usage?.value || 0), 75, 90);
renderAlerts(metrics.alerts || {});
} catch (error) {
console.error('Failed to fetch metrics:', error);
document.getElementById('alert-indicators').innerHTML = `
<div class="status-item">
<span class="status-label">Alert State</span>
<span class="status-badge error">Metrics Unavailable</span>
</div>
`;
}
}
async function checkServiceStatus() {
const services = [
{ id: 'status-api', url: HEALTH_ENDPOINT },
{ id: 'status-database', url: HEALTH_ENDPOINT },
{ id: 'status-cache', url: HEALTH_ENDPOINT },
{ id: 'status-blockchain', url: 'http://localhost:8006/v1/health' }
];
for (const service of services) {
try {
const response = await fetch(service.url, { method: 'GET', cache: 'no-cache' });
const badge = document.querySelector(`#${service.id} .status-badge`);
if (response.ok) {
badge.textContent = 'Online';
badge.className = 'status-badge success';
} else {
badge.textContent = 'Degraded';
badge.className = 'status-badge warning';
}
} catch (error) {
const badge = document.querySelector(`#${service.id} .status-badge`);
badge.textContent = 'Offline';
badge.className = 'status-badge error';
}
}
}
function renderAlerts(alerts) {
const container = document.getElementById('alert-indicators');
const entries = Object.entries(alerts);
const triggeredAlerts = entries.filter(([, alert]) => alert.triggered);
if (entries.length === 0) {
container.innerHTML = `
<div class="status-item">
<span class="status-label">Alert State</span>
<span class="status-badge warning">No Alert Data</span>
</div>
`;
return;
}
if (triggeredAlerts.length === 0) {
container.innerHTML = `
<div class="status-item">
<span class="status-label">Alert State</span>
<span class="status-badge success">All Clear</span>
</div>
`;
return;
}
container.innerHTML = triggeredAlerts.map(([name, alert]) => `
<div class="status-item">
<span class="status-label">${formatAlertName(name)}</span>
<span class="status-badge error">Critical (${alert.value} / ${alert.threshold})</span>
</div>
`).join('');
}
function formatAlertName(name) {
return name.replaceAll('_', ' ').replace(/\b\w/g, (char) => char.toUpperCase());
}
function resetMetricClasses() {
document.querySelectorAll('.metric-value').forEach((element) => {
element.classList.remove('success', 'warning', 'error');
});
}
function setMetricColor(elementId, value, warningThreshold, errorThreshold, invert = false) {
const element = document.getElementById(elementId);
const numValue = parseFloat(value);
if (invert) {
// Higher is better (e.g., cache hit rate)
if (numValue >= errorThreshold) {
element.classList.add('success');
} else if (numValue >= warningThreshold) {
element.classList.add('warning');
} else {
element.classList.add('error');
}
} else {
// Lower is better (e.g., error rate)
if (numValue <= warningThreshold) {
element.classList.add('success');
} else if (numValue <= errorThreshold) {
element.classList.add('warning');
} else {
element.classList.add('error');
}
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
refreshMetrics();
refreshInterval = setInterval(refreshMetrics, 30000); // Refresh every 30 seconds
});
</script>
</body>
</html>