package app import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/admin/dash" ) // chartCard renders one titled chart with a legend. templ chartCard(title string, icon string, width string, series []dash.ChartSeries, opts dash.ChartOptions) {
{ title }
@templ.Raw(dash.RenderChart(series, opts)) @templ.Raw(dash.RenderLegend(series))
} // statCard shows a single headline number with a colored left border. templ statCard(label string, value string, icon string, accent string) {
{ label }
{ value }
} templ monitoringHeader(title string, icon string, lastUpdated string) {

{ title }

Updated { lastUpdated }
} templ noMetricsNotice() { } templ Monitoring(data dash.MonitoringData) { @monitoringHeader("Monitoring Overview", "fa-chart-line", data.LastUpdated.Format("15:04:05")) if len(data.Overview.UnderReplicatedVolumes) == 0 && len(data.Overview.VolumeReadRate) == 0 { @noMetricsNotice() } if dash.LatestValue(data.Overview.UnderReplicatedVolumes) > 0 { }
@statCard("Under-replicated", fmt.Sprintf("%.0f", dash.LatestValue(data.Overview.UnderReplicatedVolumes)), "fa-clone", "danger") @statCard("Writable Volumes", fmt.Sprintf("%.0f", dash.LatestValue(data.Overview.WritableVolumes)), "fa-pen", "success") @statCard("Disk Usage", dash.FormatChartValue(dash.LatestValue(data.Overview.DiskUsagePct), dash.UnitPercent), "fa-hdd", "warning") @statCard("Queue Depth", fmt.Sprintf("%.0f", dash.LatestValue(data.Overview.QueueDepth)), "fa-tasks", "info")
Health
@chartCard("Under-replicated Volumes", "fa-clone", "col-xl-6", []dash.ChartSeries{ {Name: "Under-replicated", Color: dash.ChartDanger, Data: data.Overview.UnderReplicatedVolumes, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Volume Capacity Pressure", "fa-pen", "col-xl-6", []dash.ChartSeries{ {Name: "Writable", Color: dash.ChartSuccess, Data: data.Overview.WritableVolumes}, {Name: "Crowded", Color: dash.ChartWarning, Data: data.Overview.CrowdedVolumes}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Cluster Disk Usage", "fa-hdd", "col-xl-6", []dash.ChartSeries{ {Name: "Disk used", Color: dash.ChartWarning, Data: data.Overview.DiskUsagePct, Area: true}, }, dash.ChartOptions{Unit: dash.UnitPercent, Threshold: diskThreshold()}) @chartCard("Disk & Quarantine Errors", "fa-triangle-exclamation", "col-xl-6", []dash.ChartSeries{ {Name: "Disk errors", Color: dash.ChartDanger, Data: data.Overview.DiskErrors}, {Name: "Quarantined", Color: dash.ChartWarning, Data: data.Overview.Quarantined}, }, dash.ChartOptions{Unit: dash.UnitCount})
Throughput
@chartCard("Volume Server Request Rate", "fa-server", "col-xl-6", []dash.ChartSeries{ {Name: "Read", Color: dash.ChartPrimary, Data: data.Overview.VolumeReadRate}, {Name: "Write", Color: dash.ChartSuccess, Data: data.Overview.VolumeWriteRate}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Filer & S3 Request Rate", "fa-folder-open", "col-xl-6", []dash.ChartSeries{ {Name: "Filer", Color: dash.ChartInfo, Data: data.Overview.FilerRequestRate}, {Name: "S3", Color: dash.ChartPrimary, Data: data.Overview.S3RequestRate}, }, dash.ChartOptions{Unit: dash.UnitCount})
Latency
@chartCard("Volume Server Latency", "fa-stopwatch", "col-xl-6", []dash.ChartSeries{ {Name: "p50", Color: dash.ChartSuccess, Data: data.Overview.VolumeP50}, {Name: "p95", Color: dash.ChartWarning, Data: data.Overview.VolumeP95}, {Name: "p99", Color: dash.ChartDanger, Data: data.Overview.VolumeP99}, }, dash.ChartOptions{Unit: dash.UnitSeconds}) @chartCard("Filer Latency", "fa-stopwatch", "col-xl-6", []dash.ChartSeries{ {Name: "p50", Color: dash.ChartSuccess, Data: data.Overview.FilerP50}, {Name: "p95", Color: dash.ChartWarning, Data: data.Overview.FilerP95}, {Name: "p99", Color: dash.ChartDanger, Data: data.Overview.FilerP99}, }, dash.ChartOptions{Unit: dash.UnitSeconds})
Errors
@chartCard("Request Error Rate by Component", "fa-bug", "col-xl-6", []dash.ChartSeries{ {Name: "Volume", Color: dash.ChartDanger, Data: data.Overview.VolumeErrorRate}, {Name: "Filer", Color: dash.ChartWarning, Data: data.Overview.FilerErrorRate}, {Name: "S3", Color: dash.ChartInfo, Data: data.Overview.S3ErrorRate}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Maintenance Queue & Worker Slots", "fa-tasks", "col-xl-6", []dash.ChartSeries{ {Name: "Queued tasks", Color: dash.ChartInfo, Data: data.Overview.QueueDepth}, {Name: "Slots used", Color: dash.ChartPrimary, Data: data.Overview.SlotsUsed}, {Name: "Slots max", Color: dash.ChartWarning, Data: data.Overview.SlotsMax}, }, dash.ChartOptions{Unit: dash.UnitCount})
} templ MonitoringVolumeServers(data dash.MonitoringData) { @monitoringHeader("Volume Servers", "fa-server", data.LastUpdated.Format("15:04:05")) if len(data.VolumeServers) == 0 { @noMetricsNotice() }
Server Health
for _, vs := range data.VolumeServers { }
Server Request Rate p99 Latency Disk Usage Error Rate
{ vs.Address } { dash.FormatChartValue(dash.LatestValue(vs.RequestRate), dash.UnitCount) }/s { dash.FormatChartValue(dash.LatestValue(vs.P99), dash.UnitSeconds) } { dash.FormatChartValue(dash.LatestValue(vs.DiskUsagePct), dash.UnitPercent) } if dash.LatestValue(vs.ErrorRate) > 0 { { dash.FormatChartValue(dash.LatestValue(vs.ErrorRate), dash.UnitCount) }/s } else { 0 }
for _, vs := range data.VolumeServers {
{ vs.Address }
@chartCard("Request Rate", "fa-exchange-alt", "col-xl-6", []dash.ChartSeries{ {Name: "Requests", Color: dash.ChartPrimary, Data: vs.RequestRate, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("p99 Latency", "fa-stopwatch", "col-xl-6", []dash.ChartSeries{ {Name: "p99", Color: dash.ChartDanger, Data: vs.P99}, }, dash.ChartOptions{Unit: dash.UnitSeconds}) @chartCard("Disk Usage", "fa-hdd", "col-xl-6", []dash.ChartSeries{ {Name: "Disk used", Color: dash.ChartWarning, Data: vs.DiskUsagePct, Area: true}, }, dash.ChartOptions{Unit: dash.UnitPercent, Threshold: diskThreshold()}) @chartCard("Error Rate", "fa-bug", "col-xl-6", []dash.ChartSeries{ {Name: "Errors", Color: dash.ChartDanger, Data: vs.ErrorRate}, }, dash.ChartOptions{Unit: dash.UnitCount})
} } templ MonitoringFilers(data dash.MonitoringData) { @monitoringHeader("Filers", "fa-folder-open", data.LastUpdated.Format("15:04:05")) if len(data.Filers) == 0 { @noMetricsNotice() } for _, f := range data.Filers {
{ f.Address }
@chartCard("Request Rate", "fa-exchange-alt", "col-xl-6", []dash.ChartSeries{ {Name: "Requests", Color: dash.ChartInfo, Data: f.RequestRate, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Latency", "fa-stopwatch", "col-xl-6", []dash.ChartSeries{ {Name: "Filer p99", Color: dash.ChartDanger, Data: f.P99}, {Name: "Store p99", Color: dash.ChartWarning, Data: f.StoreP99}, }, dash.ChartOptions{Unit: dash.UnitSeconds}) @chartCard("In-flight Requests", "fa-spinner", "col-xl-6", []dash.ChartSeries{ {Name: "In flight", Color: dash.ChartPrimary, Data: f.InFlight, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Replication Lag", "fa-clock-rotate-left", "col-xl-6", []dash.ChartSeries{ {Name: "Lag", Color: dash.ChartWarning, Data: f.SyncLag}, }, dash.ChartOptions{Unit: dash.UnitSeconds})
} } templ MonitoringS3(data dash.MonitoringData) { @monitoringHeader("S3", "fa-cloud", data.LastUpdated.Format("15:04:05")) if len(data.S3) == 0 { @noMetricsNotice() } for _, n := range data.S3 {
{ n.Address }
@chartCard("Operation Rate", "fa-exchange-alt", "col-xl-6", []dash.ChartSeries{ {Name: "Requests", Color: dash.ChartPrimary, Data: n.RequestRate, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Error Rate", "fa-bug", "col-xl-6", []dash.ChartSeries{ {Name: "4xx", Color: dash.ChartWarning, Data: n.Errors4xx}, {Name: "5xx", Color: dash.ChartDanger, Data: n.Errors5xx}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("p99 Latency", "fa-stopwatch", "col-xl-6", []dash.ChartSeries{ {Name: "p99", Color: dash.ChartDanger, Data: n.P99}, }, dash.ChartOptions{Unit: dash.UnitSeconds})
} } templ MonitoringMasters(data dash.MonitoringData) { @monitoringHeader("Masters", "fa-crown", data.LastUpdated.Format("15:04:05")) if len(data.Masters) == 0 { @noMetricsNotice() } for _, m := range data.Masters {
{ m.Address } if m.IsLeader { Leader }
@chartCard("Heartbeat Rate", "fa-heartbeat", "col-xl-6", []dash.ChartSeries{ {Name: "Heartbeats", Color: dash.ChartSuccess, Data: m.HeartbeatRate, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Volume Creation Rate", "fa-plus", "col-xl-6", []dash.ChartSeries{ {Name: "Created", Color: dash.ChartPrimary, Data: m.VolumeCreation}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Leader Changes", "fa-random", "col-xl-6", []dash.ChartSeries{ {Name: "Changes", Color: dash.ChartDanger, Data: m.LeaderChanges}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Placement Mismatches", "fa-triangle-exclamation", "col-xl-6", []dash.ChartSeries{ {Name: "Mismatched", Color: dash.ChartWarning, Data: m.PlacementMiss}, }, dash.ChartOptions{Unit: dash.UnitCount})
} } templ MonitoringWorkers(data dash.MonitoringData) { @monitoringHeader("Workers", "fa-users-cog", data.LastUpdated.Format("15:04:05"))
@statCard("Connected Workers", fmt.Sprintf("%d", data.Workers.Connected), "fa-users", "primary") @statCard("Queue Depth", fmt.Sprintf("%.0f", dash.LatestValue(data.Workers.QueueDepth)), "fa-tasks", "info") @statCard("Slots Used", fmt.Sprintf("%.0f", dash.LatestValue(data.Workers.SlotsUsed)), "fa-microchip", "success") @statCard("Slots Max", fmt.Sprintf("%.0f", dash.LatestValue(data.Workers.SlotsMax)), "fa-layer-group", "warning")
@chartCard("Task Queue Depth", "fa-tasks", "col-xl-6", []dash.ChartSeries{ {Name: "Queued", Color: dash.ChartInfo, Data: data.Workers.QueueDepth, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Worker Slots", "fa-microchip", "col-xl-6", []dash.ChartSeries{ {Name: "Used", Color: dash.ChartPrimary, Data: data.Workers.SlotsUsed}, {Name: "Max", Color: dash.ChartWarning, Data: data.Workers.SlotsMax}, }, dash.ChartOptions{Unit: dash.UnitCount}) @chartCard("Task Completion Rate", "fa-check", "col-xl-6", []dash.ChartSeries{ {Name: "Completed", Color: dash.ChartSuccess, Data: data.Workers.TaskRate, Area: true}, }, dash.ChartOptions{Unit: dash.UnitCount})
Connected Workers
if len(data.Workers.Workers) > 0 {
for _, w := range data.Workers.Workers { }
Worker ID
{ w.ID }
} else {

No workers are currently connected.

}
}