一、概述
本讲将前三讲的日志、追踪、指标数据,以及第4-6讲的LLM/Agent监控和告警,整合为直观的仪表板。我们将实现一个轻量级实时仪表板系统。
二、架构
前端 (Chart.js + WebSocket) ←→ Go 后端 (数据聚合 + 推送) ←→ 数据存储
三、核心数据结构
// DashboardPanel 仪表板面板
type DashboardPanel struct {
ID string `json:"id"`
Title string `json:"title"`
Type string `json:"type"` // timeseries, stat, table
Width int `json:"width"` // 1-12 网格列
Height int `json:"height"`
Series []DashboardSeries `json:"series"`
Options map[string]interface{} `json:"options,omitempty"`
}
type DashboardSeries struct {
Name string `json:"name"`
Color string `json:"color"`
Unit string `json:"unit"`
Type string `json:"type"` // line, bar, area
}
// Dashboard 仪表板
type Dashboard struct {
ID string `json:"id"`
Title string `json:"title"`
Category string `json:"category"` // llm, agent, cost, alert
Panels []DashboardPanel `json:"panels"`
Refresh time.Duration `json:"refresh"`
}
四、预置仪表板
| 仪表板 | 用途 | 刷新频率 | 面板数 |
|---|---|---|---|
| LLM 总览 | Token 速率、延迟、错误率、统计 | 10s | 4 |
| Agent 监控 | 步数、时长、工具调用 | 15s | 3 |
| 成本分析 | 会话成本、日累计、构成 | 30s | 3 |
| 告警中心 | 活跃告警、趋势、MTTR | 5s | 3 |
五、后端关键代码
// 数据存储
type DataStore struct {
mu sync.RWMutex
metrics map[string][]DataPoint
maxPoints int // 默认 3600
}
// WebSocket 实时推送
type WSHub struct {
clients map[*WSClient]bool
broadcast chan []byte
}
// 每 5 秒广播一次指标数据
func (h *WSHub) PushMetrics(store *DataStore) {
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
data, _ := json.Marshal(map[string]interface{}{
"type": "metrics_update",
"metrics": store.GetRecent(5 * time.Minute),
"time": time.Now(),
})
h.broadcast <- data
}
}
// 模拟数据生成(昼夜模式)
func (dg *DataGenerator) generateMetrics() {
hour := time.Now().Hour()
baseLoad := 30000.0
if hour >= 8 && hour <= 22 {
baseLoad = 70000.0
}
// Token 速率
dg.store.AddMetric("llm_token_rate", baseLoad + rand.Float64()*20000 - 10000)
// 延迟 P50/P95/P99
dg.store.AddMetric("latency_p50", 150 + rand.Float64()*100)
dg.store.AddMetric("latency_p95", 350 + rand.Float64()*200)
dg.store.AddMetric("latency_p99", 600 + rand.Float64()*400)
// 错误率(5% 概率飙升)
errRate := 0.02 + rand.Float64()*0.08
if rand.Float64() < 0.05 { errRate = 0.15 + rand.Float64()*0.25 }
dg.store.AddMetric("llm_error_rate", errRate)
// Agent 指标
dg.store.AddMetric("agent_steps_per_session", float64(5 + rand.Intn(15)))
dg.store.AddMetric("agent_session_duration_ms", 1200 + rand.Float64()*1800)
dg.store.AddMetric("tool_calls_per_session", float64(3 + rand.Intn(10)))
// 成本
dg.store.AddMetric("cost_per_session_usd", 0.005 + rand.Float64()*0.025)
dg.store.AddMetric("daily_cost_usd", 50.0 + rand.Float64()*30)
}
六、API 端点
| 路径 | 方法 | 说明 |
|---|---|---|
/api/dashboards?category= |
GET | 获取仪表板列表 |
/api/dashboard?id= |
GET | 获取单个仪表板 |
/api/metrics?name=&duration= |
GET | 获取指标数据 |
/ws |
WebSocket | 实时数据推送 |
/ |
GET | 前端页面 |
七、前端核心逻辑
// WebSocket 连接
function connectWebSocket() {
ws = new WebSocket(`ws://${host}/ws`);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'metrics_update') updateCharts(data.metrics);
};
}
// 初始化 Chart.js 图表
function initChart(panelId, seriesNames) {
charts[panelId] = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: seriesNames.map((name, i) => ({
label: name,
data: [],
borderColor: COLORS[i],
tension: 0.4,
pointRadius: 2
}))
},
options: {
responsive: true,
animation: { duration: 300 },
scales: {
x: { type: 'time', time: { unit: 'second' }, grid: { color: '#2a3a4a' } },
y: { beginAtZero: true, grid: { color: '#2a3a4a' } }
}
}
});
}
// 每 5 秒更新图表
function updateCharts(metrics) {
Object.keys(charts).forEach(panelId => {
const chart = charts[panelId];
chart.data.labels.push(new Date().toLocaleTimeString());
if (chart.data.labels.length > 60) chart.data.labels.shift();
// 更新每个序列的最新值
panel.series.forEach((name, i) => {
const points = metrics[name] || [];
chart.data.datasets[i].data.push(points[points.length - 1]?.value || 0);
if (chart.data.datasets[i].data.length > 60) chart.data.datasets[i].data.shift();
});
chart.update('none');
});
}
八、仪表板设计原则
| 原则 | 说明 | 反例 |
|---|---|---|
| 黄金信号 | 展示 Rate/Errors/Duration | 展示 CPU 温度 |
| 层次化 | 总览 → 钻取详情 | 一张图显示所有 |
| 可操作 | 看到问题能直接定位 | 只有数字没有上下文 |
| 简洁 | 每张图 ≤ 5 条线 | 彩虹色堆叠 |
| 一致性 | 红色=错误,绿色=正常 | 颜色随意使用 |
九、启动方式
go run main.go
# 访问 http://localhost:8080
十、下讲预告
第8讲:Trace 与 Span 深度分析------链路数据的价值挖掘
我们将深入分析分布式追踪数据,学习如何从 Trace 中提取业务洞察,包括 Trace 采样策略、Span 标签分析、瓶颈识别、依赖关系图谱等。
推荐工具站:zz365.top - 在线 JSON 格式化、正则测试、时间戳转换等开发者常用工具