"use client" import { useEffect, useState, useCallback } from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { useToast } from "@/components/ui/use-toast" import { useRouter } from "next/navigation" import Link from "next/link" interface Metrics { uptime: string active_requests: number total_requests: number total_errors: number num_goroutine: number memory_usage: string avg_response_time: string requests_per_second: number bytes_per_second: number error_rate: number status_code_stats: Record recent_requests: Array<{ Time: string Path: string Status: number Latency: number BytesSent: number ClientIP: string }> latency_stats: { min: string max: string distribution: Record } bandwidth_history: Record current_bandwidth: string total_bytes: number top_referers: Array<{ path: string request_count: number error_count: number avg_latency: string bytes_transferred: number }> } export default function DashboardPage() { const [metrics, setMetrics] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const { toast } = useToast() const router = useRouter() const fetchMetrics = useCallback(async () => { try { const token = localStorage.getItem("token") if (!token) { router.push("/login") return } const response = await fetch("/admin/api/metrics", { headers: { 'Authorization': `Bearer ${token}` } }) if (response.status === 401) { localStorage.removeItem("token") router.push("/login") return } if (!response.ok) { throw new Error("加载监控数据失败") } const data = await response.json() setMetrics(data) setError(null) } catch (error) { const message = error instanceof Error ? error.message : "加载监控数据失败" setError(message) toast({ title: "错误", description: message, variant: "destructive", }) } finally { setLoading(false) } }, [router, toast]) useEffect(() => { fetchMetrics() const interval = setInterval(fetchMetrics, 3000) return () => clearInterval(interval) }, [fetchMetrics]) if (loading) { return (
加载中...
正在获取监控数据
) } if (error || !metrics) { return (
{error || "暂无数据"}
请检查后端服务是否正常运行
) } return (
基础指标
运行时间
{metrics.uptime}
当前活跃请求
{metrics.active_requests}
总传输数据
{formatBytes(metrics.total_bytes)}
每秒传输数据
{formatBytes(metrics.bytes_per_second)}/s
系统指标
Goroutine数量
{metrics.num_goroutine}
内存使用
{metrics.memory_usage}
平均响应时间
{metrics.avg_response_time}
平均每秒请求数
{metrics.requests_per_second.toFixed(2)}
状态码统计
{Object.entries(metrics.status_code_stats || {}) .sort((a, b) => a[0].localeCompare(b[0])) .map(([status, count]) => { const statusNum = parseInt(status); let colorClass = "text-green-600"; if (statusNum >= 500) { colorClass = "text-red-600"; } else if (statusNum >= 400) { colorClass = "text-yellow-600"; } else if (statusNum >= 300) { colorClass = "text-blue-600"; } // 计算总请求数 const totalRequests = Object.values(metrics.status_code_stats || {}).reduce((a, b) => a + (b as number), 0); return (
状态码 {status}
{count}
{totalRequests ? ((count as number / totalRequests) * 100).toFixed(1) : 0}%
); })}
{/* 新增:延迟统计卡片 */}
延迟统计
最小响应时间
{metrics.latency_stats?.min || "0ms"}
最大响应时间
{metrics.latency_stats?.max || "0ms"}
响应时间分布
{metrics.latency_stats?.distribution && Object.entries(metrics.latency_stats.distribution) .sort((a, b) => { // 按照延迟范围排序 const order = ["lt10ms", "10-50ms", "50-200ms", "200-1000ms", "gt1s"]; return order.indexOf(a[0]) - order.indexOf(b[0]); }) .map(([range, count]) => { // 转换桶键为更友好的显示 let displayRange = range; if (range === "lt10ms") displayRange = "<10ms"; if (range === "gt1s") displayRange = ">1s"; if (range === "200-1000ms") displayRange = "0.2-1s"; return (
{displayRange}
{count}
{Object.values(metrics.latency_stats?.distribution || {}).reduce((sum, val) => sum + val, 0) > 0 ? ((count / Object.values(metrics.latency_stats?.distribution || {}).reduce((sum, val) => sum + val, 0)) * 100).toFixed(1) : 0}%
); })}
带宽统计
当前带宽
{metrics.current_bandwidth || "0 B/s"}
带宽历史
{metrics.bandwidth_history && Object.entries(metrics.bandwidth_history) .sort((a, b) => a[0].localeCompare(b[0])) .map(([time, bandwidth]) => (
{time}
{bandwidth}
)) }
{/* 引用来源统计卡片 */} {metrics.top_referers && metrics.top_referers.length > 0 && ( 引用来源统计 (Top {metrics.top_referers.length})
{metrics.top_referers.map((referer, index) => ( ))}
来源域名 请求数 错误数 平均延迟 传输大小
{referer.path} {referer.request_count} {referer.error_count} {referer.avg_latency} {formatBytes(referer.bytes_transferred)}
)} 最近请求
{(metrics.recent_requests || []) .slice(0, 20) // 只显示最近20条记录 .map((req, index) => ( ))}
时间 路径 状态 延迟 大小 客户端IP
{formatDate(req.Time)} {req.Path} {req.Status} {formatLatency(req.Latency)} {formatBytes(req.BytesSent)} {req.ClientIP}
) } function formatBytes(bytes: number) { if (!bytes || isNaN(bytes)) return "0 B" const k = 1024 const sizes = ["B", "KB", "MB", "GB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i] } function formatDate(dateStr: string) { if (!dateStr) return "-" const date = new Date(dateStr) return date.toLocaleString() } function formatLatency(nanoseconds: number) { if (!nanoseconds || isNaN(nanoseconds)) return "-" if (nanoseconds < 1000) { return nanoseconds + " ns" } else if (nanoseconds < 1000000) { return (nanoseconds / 1000).toFixed(2) + " µs" } else if (nanoseconds < 1000000000) { return (nanoseconds / 1000000).toFixed(2) + " ms" } else { return (nanoseconds / 1000000000).toFixed(2) + " s" } } function getStatusColor(status: number) { if (status >= 500) return "bg-red-100 text-red-800" if (status >= 400) return "bg-yellow-100 text-yellow-800" if (status >= 300) return "bg-blue-100 text-blue-800" return "bg-green-100 text-green-800" }