feat(haproxy): req_tot + req_rate in Stats-API; Backend-Detail + Dashboard nutzen sie
GET /haproxy/stats liefert jetzt req_tot (kumulierte Requests seit HAProxy-Start) und req_rate (Requests/s im letzten Messfenster). Backend-Detail zeigt in der Live-Spalte 'N sess · X/s' wenn Traffic fließt; Dashboard-HAProxy-Card zeigt req/s ebenfalls an. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -32,14 +32,16 @@ const haproxyAdminSock = "/run/haproxy/admin.sock"
|
||||
// 'show stat' CSV. We only emit the fields the dashboard cares
|
||||
// about — full CSV is ~80 columns of which 90% are noise here.
|
||||
type backendStat struct {
|
||||
Backend string `json:"backend"` // pxname
|
||||
Server string `json:"server"` // svname
|
||||
Status string `json:"status"` // UP|DOWN|MAINT|...
|
||||
Sessions int64 `json:"sessions"` // current sessions (scur)
|
||||
BIn int64 `json:"bytes_in"`
|
||||
BOut int64 `json:"bytes_out"`
|
||||
LastChg int64 `json:"last_change_sec"` // seconds since last status change
|
||||
Health string `json:"health,omitempty"` // check_status (e.g. L7OK)
|
||||
Backend string `json:"backend"` // pxname
|
||||
Server string `json:"server"` // svname
|
||||
Status string `json:"status"` // UP|DOWN|MAINT|...
|
||||
Sessions int64 `json:"sessions"` // current sessions (scur)
|
||||
BIn int64 `json:"bytes_in"`
|
||||
BOut int64 `json:"bytes_out"`
|
||||
ReqTot int64 `json:"req_tot"` // total requests since start (req_tot)
|
||||
ReqRate int64 `json:"req_rate"` // requests/s last second (req_rate, server-level)
|
||||
LastChg int64 `json:"last_change_sec"` // seconds since last status change
|
||||
Health string `json:"health,omitempty"` // check_status (e.g. L7OK)
|
||||
}
|
||||
|
||||
func (h *HAProxyStatsHandler) Stats(c *gin.Context) {
|
||||
@@ -97,6 +99,8 @@ func (h *HAProxyStatsHandler) Stats(c *gin.Context) {
|
||||
Sessions: parseInt64(safeAt(fields, colIdx["scur"])),
|
||||
BIn: parseInt64(safeAt(fields, colIdx["bin"])),
|
||||
BOut: parseInt64(safeAt(fields, colIdx["bout"])),
|
||||
ReqTot: parseInt64(safeAt(fields, colIdx["req_tot"])),
|
||||
ReqRate: parseInt64(safeAt(fields, colIdx["req_rate"])),
|
||||
LastChg: parseInt64(safeAt(fields, colIdx["lastchg"])),
|
||||
Health: safeAt(fields, colIdx["check_status"]),
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ async function listDomains(): Promise<DomainFull[]> {
|
||||
interface HAProxyStat {
|
||||
backend: string; server: string; status: string
|
||||
sessions: number; bytes_in: number; bytes_out: number
|
||||
req_tot: number; req_rate: number
|
||||
last_change_sec: number; health: string
|
||||
}
|
||||
async function listHAProxyStats(): Promise<HAProxyStat[]> {
|
||||
@@ -289,9 +290,11 @@ function ServerPanel({ backendID, haproxyStats }: { backendID: number; haproxySt
|
||||
if (!stat) return <Text type="secondary" style={{ fontSize: 11 }}>—</Text>
|
||||
const color = stat.status === 'UP' ? 'green' : stat.status === 'no check' ? 'default' : 'red'
|
||||
return (
|
||||
<Tooltip title={`↓${fmtBytes(stat.bytes_in)} ↑${fmtBytes(stat.bytes_out)} · ${stat.health || stat.status}`}>
|
||||
<Tooltip title={`↓${fmtBytes(stat.bytes_in)} ↑${fmtBytes(stat.bytes_out)} · ${stat.req_tot} req total · ${stat.health || stat.status}`}>
|
||||
<Tag color={color} style={{ margin: 0, fontSize: 11 }}>{stat.status}</Tag>
|
||||
<Text type="secondary" style={{ fontSize: 11, marginLeft: 4 }}>{stat.sessions} sess</Text>
|
||||
<Text type="secondary" style={{ fontSize: 11, marginLeft: 4 }}>
|
||||
{stat.sessions} sess{stat.req_rate > 0 ? ` · ${stat.req_rate}/s` : ''}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -101,6 +101,7 @@ interface AuditEntry {
|
||||
interface HAProxyBackend {
|
||||
backend: string; server: string; status: string
|
||||
sessions: number; bytes_in: number; bytes_out: number
|
||||
req_tot: number; req_rate: number
|
||||
last_change_sec: number; health?: string
|
||||
}
|
||||
|
||||
@@ -407,7 +408,7 @@ export default function DashboardPage() {
|
||||
style={{ margin: 0, fontSize: 10 }}
|
||||
>{b.status}</Tag>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>
|
||||
{b.sessions} sess · ↓{formatBytes(b.bytes_in)} ↑{formatBytes(b.bytes_out)}
|
||||
{b.sessions} sess{b.req_rate > 0 ? ` · ${b.req_rate}/s` : ''} · ↓{formatBytes(b.bytes_in)} ↑{formatBytes(b.bytes_out)}
|
||||
</Text>
|
||||
<Text type="secondary" style={{ fontSize: 11, marginLeft: 'auto' }}>{formatUptime(b.last_change_sec)}</Text>
|
||||
</Space>
|
||||
|
||||
Reference in New Issue
Block a user