Files
buque/internal/cmd/stats.go
2025-11-02 01:41:51 +01:00

132 líneas
3.5 KiB
Go

package cmd
import (
"context"
"fmt"
"os"
"text/tabwriter"
"time"
"github.com/spf13/cobra"
"github.com/manalejandro/buque/internal/models"
"github.com/manalejandro/buque/internal/stats"
)
var statsCmd = &cobra.Command{
Use: "stats [environment]",
Short: "Show container statistics",
Long: `Display resource usage statistics for containers. If an environment is specified, shows only containers from that environment.`,
RunE: func(cmd *cobra.Command, args []string) error {
collector, err := stats.NewCollector()
if err != nil {
return err
}
defer collector.Close()
ctx := context.Background()
continuous, _ := cmd.Flags().GetBool("continuous")
interval, _ := cmd.Flags().GetInt("interval")
sortBy, _ := cmd.Flags().GetString("sort")
if continuous {
// Clear screen and show stats continuously
fmt.Print("\033[2J") // Clear screen
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
fmt.Print("\033[H") // Move cursor to home position
if err := displayStats(ctx, collector, args, sortBy); err != nil {
return err
}
fmt.Printf("\nRefreshing every %d seconds... (Press Ctrl+C to exit)\n", interval)
}
}
}
return displayStats(ctx, collector, args, sortBy)
},
}
func displayStats(ctx context.Context, collector *stats.Collector, args []string, sortBy string) error {
var containerStats []models.ContainerStats
var err error
if len(args) > 0 {
// Show stats for specific environment
containerStats, err = collector.CollectForEnvironment(ctx, args[0])
} else {
// Show stats for all containers
containerStats, err = collector.CollectAll(ctx)
}
if err != nil {
return err
}
if len(containerStats) == 0 {
fmt.Println("No running containers found.")
return nil
}
// Sort stats
containerStats = collector.SortStats(containerStats, sortBy, true)
// Display in table format
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "CONTAINER\tENVIRONMENT\tCPU %\tMEMORY USAGE\tMEMORY %\tNET I/O\tBLOCK I/O")
for _, stat := range containerStats {
netIO := fmt.Sprintf("%s / %s",
stats.FormatBytes(stat.NetworkRx),
stats.FormatBytes(stat.NetworkTx))
blockIO := fmt.Sprintf("%s / %s",
stats.FormatBytes(stat.BlockRead),
stats.FormatBytes(stat.BlockWrite))
memUsage := fmt.Sprintf("%s / %s",
stats.FormatBytes(stat.MemoryUsage),
stats.FormatBytes(stat.MemoryLimit))
fmt.Fprintf(w, "%s\t%s\t%.2f%%\t%s\t%.2f%%\t%s\t%s\n",
stat.Name,
stat.Environment,
stat.CPUPercentage,
memUsage,
stat.MemoryPercent,
netIO,
blockIO,
)
}
w.Flush()
// Show aggregated stats
aggStats, err := collector.GetAggregatedStats(ctx)
if err == nil {
fmt.Printf("\nTotal Containers: %d\n", aggStats.TotalContainers)
fmt.Printf("Total CPU: %.2f%%\n", aggStats.TotalCPUPercent)
fmt.Printf("Total Memory: %s / %s (%.2f%%)\n",
stats.FormatBytes(aggStats.TotalMemoryUsage),
stats.FormatBytes(aggStats.TotalMemoryLimit),
aggStats.TotalMemoryPercent)
fmt.Printf("Total Network: %s / %s\n",
stats.FormatBytes(aggStats.TotalNetworkRx),
stats.FormatBytes(aggStats.TotalNetworkTx))
}
return nil
}
func init() {
statsCmd.Flags().BoolP("continuous", "c", false, "Continuous monitoring mode")
statsCmd.Flags().IntP("interval", "i", 2, "Refresh interval in seconds (for continuous mode)")
statsCmd.Flags().StringP("sort", "s", "cpu", "Sort by: cpu, memory, network, name")
}