// ABOUTME: Emits a RunSummary as a structured log entry via slog. // ABOUTME: Follows the same slog pattern as internal/output/logger.go for consistency. package summary import ( "io" "log/slog" ) // SummaryWriter outputs a RunSummary using structured logging type SummaryWriter struct { logger *slog.Logger } // NewSummaryWriter creates a writer that emits summaries to the given output in the given format func NewSummaryWriter(output io.Writer, format string) *SummaryWriter { opts := &slog.HandlerOptions{Level: slog.LevelInfo} var handler slog.Handler switch format { case "text": handler = slog.NewTextHandler(output, opts) default: handler = slog.NewJSONHandler(output, opts) } return &SummaryWriter{ logger: slog.New(handler), } } // Write emits the run summary as a single structured log entry func (w *SummaryWriter) Write(s *RunSummary) { if s == nil { return } w.logger.Info("run_summary", slog.Time("start_time", s.StartTime), slog.Time("end_time", s.EndTime), slog.Float64("duration_seconds", s.DurationSeconds), slog.Int("sample_count", s.SampleCount), slog.Group("cpu_total_percent", slog.Float64("peak", s.CPUTotal.Peak), slog.Float64("avg", s.CPUTotal.Avg), slog.Float64("p95", s.CPUTotal.P95), ), slog.Group("mem_used_bytes", slog.Float64("peak", s.MemUsedBytes.Peak), slog.Float64("avg", s.MemUsedBytes.Avg), slog.Float64("p95", s.MemUsedBytes.P95), ), slog.Group("mem_used_percent", slog.Float64("peak", s.MemUsedPercent.Peak), slog.Float64("avg", s.MemUsedPercent.Avg), slog.Float64("p95", s.MemUsedPercent.P95), ), slog.Any("top_cpu_processes", s.TopCPUProcesses), slog.Any("top_mem_processes", s.TopMemProcesses), ) }