package exporter import ( "encoding/csv" "encoding/json" "fmt" "io" "git.mchus.pro/mchus/logpile/internal/models" ) // Exporter handles data export in various formats type Exporter struct { result *models.AnalysisResult } // New creates a new exporter func New(result *models.AnalysisResult) *Exporter { return &Exporter{result: result} } // ExportCSV exports serial numbers to CSV format func (e *Exporter) ExportCSV(w io.Writer) error { writer := csv.NewWriter(w) defer writer.Flush() // Header if err := writer.Write([]string{"Component", "Serial Number", "Manufacturer", "Part Number"}); err != nil { return err } if e.result == nil { return nil } // FRU data for _, fru := range e.result.FRU { if fru.SerialNumber == "" { continue } name := fru.ProductName if name == "" { name = fru.Description } if err := writer.Write([]string{ name, fru.SerialNumber, fru.Manufacturer, fru.PartNumber, }); err != nil { return err } } // Hardware data if e.result.Hardware != nil { // Memory for _, mem := range e.result.Hardware.Memory { if mem.SerialNumber == "" { continue } if err := writer.Write([]string{ fmt.Sprintf("DIMM Slot %d (%s)", mem.Slot, mem.PartNumber), mem.SerialNumber, mem.Manufacturer, mem.PartNumber, }); err != nil { return err } } // Storage for _, stor := range e.result.Hardware.Storage { if stor.SerialNumber == "" { continue } if err := writer.Write([]string{ fmt.Sprintf("%s %s", stor.Type, stor.Model), stor.SerialNumber, "", "", }); err != nil { return err } } // PCIe devices for _, pcie := range e.result.Hardware.PCIeDevices { if pcie.SerialNumber == "" { continue } if err := writer.Write([]string{ fmt.Sprintf("%s (%s)", pcie.DeviceClass, pcie.Slot), pcie.SerialNumber, "", pcie.PartNumber, }); err != nil { return err } } } return nil } // ExportJSON exports all data to JSON format func (e *Exporter) ExportJSON(w io.Writer) error { encoder := json.NewEncoder(w) encoder.SetIndent("", " ") return encoder.Encode(e.result) } // ExportTXT exports a human-readable text report func (e *Exporter) ExportTXT(w io.Writer) error { fmt.Fprintln(w, "LOGPile Analysis Report") fmt.Fprintln(w, "========================") fmt.Fprintln(w) if e.result == nil { fmt.Fprintln(w, "No data loaded.") return nil } fmt.Fprintf(w, "File: %s\n\n", e.result.Filename) // Hardware summary if e.result.Hardware != nil { hw := e.result.Hardware // Firmware if len(hw.Firmware) > 0 { fmt.Fprintln(w, "FIRMWARE VERSIONS") fmt.Fprintln(w, "-----------------") for _, fw := range hw.Firmware { fmt.Fprintf(w, " %s: %s\n", fw.DeviceName, fw.Version) } fmt.Fprintln(w) } // CPUs if len(hw.CPUs) > 0 { fmt.Fprintln(w, "PROCESSORS") fmt.Fprintln(w, "----------") for _, cpu := range hw.CPUs { fmt.Fprintf(w, " Socket %d: %s\n", cpu.Socket, cpu.Model) fmt.Fprintf(w, " Cores: %d, Threads: %d, Freq: %d MHz (Turbo: %d MHz)\n", cpu.Cores, cpu.Threads, cpu.FrequencyMHz, cpu.MaxFreqMHz) fmt.Fprintf(w, " TDP: %dW, L3 Cache: %d KB\n", cpu.TDP, cpu.L3CacheKB) } fmt.Fprintln(w) } // Memory if len(hw.Memory) > 0 { fmt.Fprintln(w, "MEMORY") fmt.Fprintln(w, "------") totalMB := 0 for _, mem := range hw.Memory { totalMB += mem.SizeMB } fmt.Fprintf(w, " Total: %d GB (%d DIMMs)\n", totalMB/1024, len(hw.Memory)) fmt.Fprintf(w, " Type: %s @ %d MHz\n", hw.Memory[0].Type, hw.Memory[0].CurrentSpeedMHz) fmt.Fprintf(w, " Manufacturer: %s\n", hw.Memory[0].Manufacturer) fmt.Fprintln(w) } // Storage if len(hw.Storage) > 0 { fmt.Fprintln(w, "STORAGE") fmt.Fprintln(w, "-------") for _, stor := range hw.Storage { fmt.Fprintf(w, " %s: %s (%d GB) - S/N: %s\n", stor.Slot, stor.Model, stor.SizeGB, stor.SerialNumber) } fmt.Fprintln(w) } // PCIe if len(hw.PCIeDevices) > 0 { fmt.Fprintln(w, "PCIE DEVICES") fmt.Fprintln(w, "------------") for _, pcie := range hw.PCIeDevices { fmt.Fprintf(w, " %s: %s (x%d %s)\n", pcie.Slot, pcie.DeviceClass, pcie.LinkWidth, pcie.LinkSpeed) if pcie.SerialNumber != "" { fmt.Fprintf(w, " S/N: %s\n", pcie.SerialNumber) } if len(pcie.MACAddresses) > 0 { fmt.Fprintf(w, " MACs: %v\n", pcie.MACAddresses) } } fmt.Fprintln(w) } } // Sensors summary if len(e.result.Sensors) > 0 { fmt.Fprintln(w, "SENSOR READINGS") fmt.Fprintln(w, "---------------") // Group by type byType := make(map[string][]models.SensorReading) for _, s := range e.result.Sensors { byType[s.Type] = append(byType[s.Type], s) } for stype, sensors := range byType { fmt.Fprintf(w, "\n %s:\n", stype) for _, s := range sensors { if s.Value != 0 { fmt.Fprintf(w, " %s: %.0f %s [%s]\n", s.Name, s.Value, s.Unit, s.Status) } else if s.RawValue != "" { fmt.Fprintf(w, " %s: %s [%s]\n", s.Name, s.RawValue, s.Status) } } } fmt.Fprintln(w) } // FRU summary if len(e.result.FRU) > 0 { fmt.Fprintln(w, "FRU COMPONENTS") fmt.Fprintln(w, "--------------") for _, fru := range e.result.FRU { name := fru.ProductName if name == "" { name = fru.Description } fmt.Fprintf(w, " %s\n", name) if fru.SerialNumber != "" { fmt.Fprintf(w, " Serial: %s\n", fru.SerialNumber) } if fru.Manufacturer != "" { fmt.Fprintf(w, " Manufacturer: %s\n", fru.Manufacturer) } } fmt.Fprintln(w) } // Events summary fmt.Fprintf(w, "EVENTS: %d total\n", len(e.result.Events)) var critical, warning, info int for _, ev := range e.result.Events { switch ev.Severity { case models.SeverityCritical: critical++ case models.SeverityWarning: warning++ case models.SeverityInfo: info++ } } fmt.Fprintf(w, " Critical: %d\n", critical) fmt.Fprintf(w, " Warning: %d\n", warning) fmt.Fprintf(w, " Info: %d\n", info) return nil }