|
|
|
@ -1,6 +1,15 @@
|
|
|
|
|
package commands
|
|
|
|
|
|
|
|
|
|
import "time"
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/carlosms/asciigraph"
|
|
|
|
|
"github.com/fatih/color"
|
|
|
|
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// ContainerStats autogenerated at https://mholt.github.io/json-to-go/
|
|
|
|
|
type ContainerStats struct {
|
|
|
|
@ -125,10 +134,96 @@ func (s *ContainerStats) CalculateContainerCPUPercentage() float64 {
|
|
|
|
|
cpuTotalUsageDelta := s.CPUStats.SystemCPUUsage - s.PrecpuStats.SystemCPUUsage
|
|
|
|
|
numberOfCores := len(s.CPUStats.CPUUsage.PercpuUsage)
|
|
|
|
|
|
|
|
|
|
return float64(cpuUsageDelta*100) * float64(numberOfCores) / float64(cpuTotalUsageDelta)
|
|
|
|
|
value := float64(cpuUsageDelta*100) * float64(numberOfCores) / float64(cpuTotalUsageDelta)
|
|
|
|
|
if math.IsNaN(value) {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CalculateContainerMemoryUsage calculates the memory usage of the container as a percent of total available memory
|
|
|
|
|
func (s *ContainerStats) CalculateContainerMemoryUsage() float64 {
|
|
|
|
|
return float64(s.MemoryStats.Usage*100) / float64(s.MemoryStats.Limit)
|
|
|
|
|
value := float64(s.MemoryStats.Usage*100) / float64(s.MemoryStats.Limit)
|
|
|
|
|
if math.IsNaN(value) {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RenderStats returns a string containing the rendered stats of the container
|
|
|
|
|
func (s *ContainerStats) RenderStats(viewWidth int, cpuUsageHistory []float64, memoryUsageHistory []float64) (string, error) {
|
|
|
|
|
percentMemory := s.CalculateContainerMemoryUsage()
|
|
|
|
|
memoryUsageHistory = append(memoryUsageHistory, percentMemory)
|
|
|
|
|
memoryGraph := asciigraph.Plot(
|
|
|
|
|
memoryUsageHistory,
|
|
|
|
|
asciigraph.Height(10),
|
|
|
|
|
asciigraph.Width(viewWidth-10),
|
|
|
|
|
asciigraph.Min(0),
|
|
|
|
|
asciigraph.Max(100),
|
|
|
|
|
asciigraph.Caption(
|
|
|
|
|
fmt.Sprintf(
|
|
|
|
|
"%.2f%% Memory (%s/%s)",
|
|
|
|
|
percentMemory,
|
|
|
|
|
formatBinaryBytes(s.MemoryStats.Usage),
|
|
|
|
|
formatBinaryBytes(int(s.MemoryStats.Limit)),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
percentageCPU := s.CalculateContainerCPUPercentage()
|
|
|
|
|
cpuUsageHistory = append(cpuUsageHistory, percentageCPU)
|
|
|
|
|
cpuGraph := asciigraph.Plot(
|
|
|
|
|
cpuUsageHistory,
|
|
|
|
|
asciigraph.Height(10),
|
|
|
|
|
asciigraph.Width(viewWidth-10),
|
|
|
|
|
asciigraph.Min(0),
|
|
|
|
|
asciigraph.Max(100),
|
|
|
|
|
asciigraph.Caption(fmt.Sprintf("%.2f%% CPU", percentageCPU)),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
pidsCount := fmt.Sprintf("PIDs: %d", s.PidsStats.Current)
|
|
|
|
|
dataReceived := fmt.Sprintf("Traffic received: %s", formatDecimalBytes(s.Networks.Eth0.RxBytes))
|
|
|
|
|
dataSent := fmt.Sprintf("Traffic sent: %s", formatDecimalBytes(s.Networks.Eth0.TxBytes))
|
|
|
|
|
|
|
|
|
|
originalJSON, err := json.MarshalIndent(s, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contents := fmt.Sprintf("\n\n%s\n\n%s\n\n%s\n\n%s\n%s\n\n%s",
|
|
|
|
|
utils.ColoredString(cpuGraph, color.FgCyan),
|
|
|
|
|
utils.ColoredString(memoryGraph, color.FgGreen),
|
|
|
|
|
pidsCount,
|
|
|
|
|
dataReceived,
|
|
|
|
|
dataSent,
|
|
|
|
|
string(originalJSON),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return contents, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func formatBinaryBytes(b int) string {
|
|
|
|
|
n := float64(b)
|
|
|
|
|
units := []string{"B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
|
|
|
|
for _, unit := range units {
|
|
|
|
|
if n > math.Pow(2, 10) {
|
|
|
|
|
n = n / math.Pow(2, 10)
|
|
|
|
|
} else {
|
|
|
|
|
return fmt.Sprintf("%.2f%s", n, unit)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "a lot"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func formatDecimalBytes(b int) string {
|
|
|
|
|
n := float64(b)
|
|
|
|
|
units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
|
|
|
|
for _, unit := range units {
|
|
|
|
|
if n > math.Pow(10, 3) {
|
|
|
|
|
n = n / math.Pow(10, 3)
|
|
|
|
|
} else {
|
|
|
|
|
return fmt.Sprintf("%.2f%s", n, unit)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "a lot"
|
|
|
|
|
}
|
|
|
|
|