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]) }