mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
more progress
This commit is contained in:
parent
88b46a79fe
commit
8c34a5d9f7
4 changed files with 130 additions and 35 deletions
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/carlosms/asciigraph"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
|
@ -118,7 +116,7 @@ func (gui *Gui) renderStats(mainView *gocui.View, container *commands.Container,
|
|||
mainView.Autoscroll = false
|
||||
mainView.Title = "Stats"
|
||||
|
||||
stats, err := gui.DockerCommand.Client.ContainerStats(context.Background(), container.ID, true)
|
||||
stream, err := gui.DockerCommand.Client.ContainerStats(context.Background(), container.ID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -126,7 +124,7 @@ func (gui *Gui) renderStats(mainView *gocui.View, container *commands.Container,
|
|||
go func() {
|
||||
cpuUsageHistory := []float64{}
|
||||
memoryUsageHistory := []float64{}
|
||||
scanner := bufio.NewScanner(stats.Body)
|
||||
scanner := bufio.NewScanner(stream.Body)
|
||||
for scanner.Scan() {
|
||||
data := scanner.Bytes()
|
||||
var stats commands.ContainerStats
|
||||
|
|
@ -140,34 +138,22 @@ func (gui *Gui) renderStats(mainView *gocui.View, container *commands.Container,
|
|||
memoryUsageHistory = memoryUsageHistory[1:]
|
||||
}
|
||||
|
||||
percentMemory := stats.CalculateContainerMemoryUsage()
|
||||
width, _ := mainView.Size()
|
||||
|
||||
memoryUsageHistory = append(memoryUsageHistory, percentMemory)
|
||||
memoryGraph := asciigraph.Plot(
|
||||
memoryUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
asciigraph.Width(30),
|
||||
asciigraph.Min(0),
|
||||
asciigraph.Max(100),
|
||||
asciigraph.Caption(fmt.Sprintf("%.2f%% Memory (%.2f/%.2f)", percentMemory, float64(stats.MemoryStats.Usage)/math.Pow(2, 20), float64(stats.MemoryStats.Limit)/math.Pow(2, 20))),
|
||||
)
|
||||
contents, err := stats.RenderStats(width, cpuUsageHistory, memoryUsageHistory)
|
||||
if err != nil {
|
||||
gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
percentageCPU := stats.CalculateContainerCPUPercentage()
|
||||
if gui.State.MainWriterID != writerID {
|
||||
stream.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
cpuUsageHistory = append(cpuUsageHistory, percentageCPU)
|
||||
cpuGraph := asciigraph.Plot(
|
||||
cpuUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
asciigraph.Width(30),
|
||||
asciigraph.Min(0),
|
||||
asciigraph.Max(100),
|
||||
asciigraph.Caption(fmt.Sprintf("%.2f%% CPU", percentageCPU)),
|
||||
)
|
||||
|
||||
gui.renderString(gui.g, "main", fmt.Sprintf("%s\n%s", cpuGraph, memoryGraph))
|
||||
gui.reRenderString(gui.g, "main", contents)
|
||||
}
|
||||
|
||||
stats.Body.Close()
|
||||
stream.Body.Close()
|
||||
}()
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ func (gui *Gui) Run() error {
|
|||
// TODO: see if this is right
|
||||
// gui.goEvery(time.Second*10, gui.refreshContainers)
|
||||
gui.goEvery(time.Millisecond*50, gui.renderAppStatus)
|
||||
gui.goEvery(time.Millisecond*10, gui.reRender)
|
||||
gui.goEvery(time.Millisecond*30, gui.reRenderMain)
|
||||
}()
|
||||
|
||||
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
|
||||
|
|
@ -493,10 +493,12 @@ func (gui *Gui) Run() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (gui *Gui) reRender() error {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
return nil
|
||||
})
|
||||
func (gui *Gui) reRenderMain() error {
|
||||
if gui.getMainView().IsTainted() {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,18 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// reRenderString sets the view's content, without changing its origin
|
||||
func (gui *Gui) reRenderString(g *gocui.Gui, viewName, s string) error {
|
||||
g.Update(func(*gocui.Gui) error {
|
||||
v, err := g.View(viewName)
|
||||
if err != nil {
|
||||
return nil // return gracefully if view has been deleted
|
||||
}
|
||||
return gui.setViewContent(gui.g, v, s)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
|
||||
optionsArray := make([]string, 0)
|
||||
for key, description := range optionsMap {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue