refactor(metrics): enhance dashboard styles and optimize SQL queries

- Updated CSS styles for the metrics dashboard, improving layout with flex properties and enhanced item presentation.
- Modified the status code display to use a more organized structure, allowing for better alignment and spacing.
- Changed the data type for avgLatency in SaveMetrics to int64 for consistency.
- Implemented a context with timeout for database queries in GetRecentMetrics, improving performance and reliability.
- Optimized SQL queries to limit result sets and enhance data retrieval efficiency.

These changes improve the user experience and data presentation in the metrics dashboard, providing clearer insights into performance metrics.
This commit is contained in:
wood chen 2024-12-05 09:22:33 +08:00
parent 7cca4bab95
commit 8df86387ac
2 changed files with 35 additions and 29 deletions

View File

@ -396,30 +396,35 @@ var metricsTemplate = `
.status-row { .status-row {
display: flex; display: flex;
flex-direction: column; flex-wrap: wrap;
gap: 12px; gap: 15px;
justify-content: flex-start;
} }
.status-labels, .status-values { .status-item {
display: flex; display: flex;
justify-content: flex-start; flex-direction: column;
gap: 20px; align-items: center;
background: white;
padding: 10px;
border-radius: 6px;
min-width: 80px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
} }
.status-badge { .status-badge {
flex: 0 0 auto;
text-align: center; text-align: center;
padding: 4px 12px; padding: 4px 12px;
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
color: white; color: white;
min-width: 60px; margin-bottom: 5px;
} }
.metric-value { .metric-value {
flex: 0 0 auto;
text-align: center; text-align: center;
min-width: 60px; font-weight: bold;
color: #666;
} }
.loading { .loading {
position: relative; position: relative;
@ -687,24 +692,18 @@ var metricsTemplate = `
// 更新状态码计 // 更新状态码计
const statusCodesHtml = '<div class="status-row">' + const statusCodesHtml = '<div class="status-row">' +
'<div class="status-labels">' +
Object.entries(data.status_code_stats) Object.entries(data.status_code_stats)
.sort((a, b) => a[0].localeCompare(b[0])) .sort((a, b) => a[0].localeCompare(b[0]))
.map(([status, _]) => { .map(([status, count]) => {
const firstDigit = status.charAt(0); const firstDigit = status.charAt(0);
const statusClass = (firstDigit >= '2' && firstDigit <= '5') const statusClass = (firstDigit >= '2' && firstDigit <= '5')
? 'status-' + firstDigit + 'xx' ? 'status-' + firstDigit + 'xx'
: 'status-other'; : 'status-other';
return '<span class="status-badge ' + statusClass + '">' + status + '</span>'; return '<div class="status-item">' +
'<span class="status-badge ' + statusClass + '">' + status + '</span>' +
'<span class="metric-value">' + count.toLocaleString() + '</span>' +
'</div>';
}).join('') + }).join('') +
'</div>' +
'<div class="status-values">' +
Object.entries(data.status_code_stats)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([_, count]) => {
return '<span class="metric-value">' + count.toLocaleString() + '</span>';
}).join('') +
'</div>' +
'</div>'; '</div>';
const statusCodesContainer = document.getElementById('statusCodes'); const statusCodesContainer = document.getElementById('statusCodes');

View File

@ -1,6 +1,7 @@
package models package models
import ( import (
"context"
"database/sql" "database/sql"
"fmt" "fmt"
"log" "log"
@ -405,7 +406,7 @@ func (db *MetricsDB) SaveMetrics(stats map[string]interface{}) error {
if !ok { if !ok {
return fmt.Errorf("invalid total_bytes type") return fmt.Errorf("invalid total_bytes type")
} }
avgLatency, ok := stats["avg_latency"].(float64) avgLatency, ok := stats["avg_latency"].(int64)
if !ok { if !ok {
return fmt.Errorf("invalid avg_latency type") return fmt.Errorf("invalid avg_latency type")
} }
@ -421,7 +422,7 @@ func (db *MetricsDB) SaveMetrics(stats map[string]interface{}) error {
totalReqs, totalReqs,
totalErrs, totalErrs,
totalBytes, totalBytes,
int64(avgLatency), avgLatency,
errorRate, errorRate,
) )
if err != nil { if err != nil {
@ -443,6 +444,10 @@ func (db *MetricsDB) GetRecentMetrics(hours float64) ([]HistoricalMetrics, error
cacheSize int64 cacheSize int64
} }
// 添加查询超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 处理小于1小时的情况 // 处理小于1小时的情况
if hours <= 0 { if hours <= 0 {
hours = 0.5 // 30分钟 hours = 0.5 // 30分钟
@ -469,8 +474,8 @@ func (db *MetricsDB) GetRecentMetrics(hours float64) ([]HistoricalMetrics, error
timeStep = 1440 // 1天 timeStep = 1440 // 1天
} }
// 修改查询逻辑,使用 strftime 来处理时间 // 修改查询逻辑,优化性能
rows, err := db.DB.Query(` rows, err := db.DB.QueryContext(ctx, `
WITH RECURSIVE WITH RECURSIVE
time_points(ts) AS ( time_points(ts) AS (
SELECT strftime(?, 'now', 'localtime') SELECT strftime(?, 'now', 'localtime')
@ -478,7 +483,7 @@ func (db *MetricsDB) GetRecentMetrics(hours float64) ([]HistoricalMetrics, error
SELECT strftime(?, datetime(ts, '-' || ? || ' minutes')) SELECT strftime(?, datetime(ts, '-' || ? || ' minutes'))
FROM time_points FROM time_points
WHERE ts > strftime(?, datetime('now', '-' || ? || ' hours', 'localtime')) WHERE ts > strftime(?, datetime('now', '-' || ? || ' hours', 'localtime'))
LIMIT 1000 LIMIT ? -- 限制时间点数量
), ),
grouped_metrics AS ( grouped_metrics AS (
SELECT SELECT
@ -489,7 +494,9 @@ func (db *MetricsDB) GetRecentMetrics(hours float64) ([]HistoricalMetrics, error
AVG(avg_latency) as avg_latency AVG(avg_latency) as avg_latency
FROM metrics_history FROM metrics_history
WHERE timestamp >= datetime('now', '-' || ? || ' hours', 'localtime') WHERE timestamp >= datetime('now', '-' || ? || ' hours', 'localtime')
AND timestamp < datetime('now', 'localtime') -- 添加上限
GROUP BY group_time GROUP BY group_time
LIMIT ? -- 限制结果数量
) )
SELECT SELECT
tp.ts as timestamp, tp.ts as timestamp,
@ -500,8 +507,8 @@ func (db *MetricsDB) GetRecentMetrics(hours float64) ([]HistoricalMetrics, error
FROM time_points tp FROM time_points tp
LEFT JOIN grouped_metrics m ON tp.ts = m.group_time LEFT JOIN grouped_metrics m ON tp.ts = m.group_time
ORDER BY timestamp DESC ORDER BY timestamp DESC
LIMIT 1000 LIMIT ?
`, interval, interval, timeStep, interval, hours, interval, hours) `, interval, interval, timeStep, interval, hours, 1000, interval, hours, 1000, 1000)
if err != nil { if err != nil {
return nil, err return nil, err
} }