Files
storage-appliance/internal/monitoring/ui.go

150 lines
3.3 KiB
Go

package monitoring
import (
"context"
"fmt"
"strings"
"time"
)
// UIMetric represents a metric for UI display
type UIMetric struct {
Name string
Value string
Status string // "ok", "warning", "error"
Timestamp time.Time
Error string
}
// UIMetricGroup represents a group of metrics for UI display
type UIMetricGroup struct {
Title string
Metrics []UIMetric
Errors []string
}
// UIExporter exports metrics in a format suitable for UI display
type UIExporter struct {
Collectors []Collector
}
func NewUIExporter(collectors ...Collector) *UIExporter {
return &UIExporter{Collectors: collectors}
}
// Export collects all metrics and formats them for UI
func (e *UIExporter) Export(ctx context.Context) []UIMetricGroup {
groups := []UIMetricGroup{}
for _, collector := range e.Collectors {
collection := collector.Collect(ctx)
// Capitalize first letter
name := collector.Name()
if len(name) > 0 {
name = strings.ToUpper(name[:1]) + name[1:]
}
group := UIMetricGroup{
Title: name,
Metrics: []UIMetric{},
Errors: collection.Errors,
}
for _, metric := range collection.Metrics {
status := "ok"
value := formatMetricValue(metric)
// Determine status based on metric type and value
if metric.Name == "zfs_pool_health" {
if metric.Value == 0.0 {
status = "error"
} else if metric.Value == 0.5 {
status = "warning"
}
} else if metric.Name == "smart_health" {
if metric.Value == 0.0 {
status = "error"
}
} else if metric.Name == "service_state" {
if metric.Value == 0.0 {
status = "error"
}
} else if strings.HasPrefix(metric.Name, "host_load") {
if metric.Value > 10.0 {
status = "warning"
}
if metric.Value > 20.0 {
status = "error"
}
}
group.Metrics = append(group.Metrics, UIMetric{
Name: formatMetricName(metric),
Value: value,
Status: status,
Timestamp: time.Now(),
})
}
groups = append(groups, group)
}
return groups
}
func formatMetricName(metric MetricValue) string {
name := metric.Name
if len(metric.Labels) > 0 {
labels := []string{}
for k, v := range metric.Labels {
labels = append(labels, fmt.Sprintf("%s=%s", k, v))
}
name = fmt.Sprintf("%s{%s}", name, strings.Join(labels, ", "))
}
return name
}
func formatMetricValue(metric MetricValue) string {
switch metric.Name {
case "zfs_pool_health":
if metric.Value == 1.0 {
return "ONLINE"
} else if metric.Value == 0.5 {
return "DEGRADED"
}
return "FAULTED"
case "zfs_pool_scrub_in_progress":
if metric.Value == 1.0 {
return "In Progress"
}
return "Idle"
case "smart_health":
if metric.Value == 1.0 {
return "PASSED"
}
return "FAILED"
case "service_state":
if metric.Value == 1.0 {
return "Running"
}
return "Stopped"
case "host_load1", "host_load5", "host_load15":
return fmt.Sprintf("%.2f", metric.Value)
case "host_memory_total_bytes", "host_memory_free_bytes", "host_memory_available_bytes":
return formatBytes(metric.Value)
default:
return fmt.Sprintf("%.2f", metric.Value)
}
}
func formatBytes(bytes float64) string {
units := []string{"B", "KB", "MB", "GB", "TB"}
value := bytes
unit := 0
for value >= 1024 && unit < len(units)-1 {
value /= 1024
unit++
}
return fmt.Sprintf("%.2f %s", value, units[unit])
}