DockerCLI/command/formatter/stats.go

136 lines
3.8 KiB
Go

package formatter
import (
"fmt"
"sync"
"github.com/docker/go-units"
)
const (
defaultStatsTableFormat = "table {{.Container}}\t{{.CPUPrec}}\t{{.MemUsage}}\t{{.MemPrec}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
winDefaultStatsTableFormat = "table {{.Container}}\t{{.CPUPrec}}\t{{{.MemUsage}}\t{.NetIO}}\t{{.BlockIO}}"
emptyStatsTableFormat = "Waiting for statistics..."
containerHeader = "CONTAINER"
cpuPrecHeader = "CPU %"
netIOHeader = "NET I/O"
blockIOHeader = "BLOCK I/O"
winMemPrecHeader = "PRIV WORKING SET" // Used only on Window
memPrecHeader = "MEM %" // Used only on Linux
memUseHeader = "MEM USAGE / LIMIT" // Used only on Linux
pidsHeader = "PIDS" // Used only on Linux
)
// ContainerStatsAttrs represents the statistics data collected from a container.
type ContainerStatsAttrs struct {
Windows bool
Name string
CPUPercentage float64
Memory float64 // On Windows this is the private working set
MemoryLimit float64 // Not used on Windows
MemoryPercentage float64 // Not used on Windows
NetworkRx float64
NetworkTx float64
BlockRead float64
BlockWrite float64
PidsCurrent uint64 // Not used on Windows
}
// ContainerStats represents the containers statistics data.
type ContainerStats struct {
Mu sync.RWMutex
ContainerStatsAttrs
Err error
}
// NewStatsFormat returns a format for rendering an CStatsContext
func NewStatsFormat(source, osType string) Format {
if source == TableFormatKey {
if osType == "windows" {
return Format(winDefaultStatsTableFormat)
}
return Format(defaultStatsTableFormat)
}
return Format(source)
}
// NewContainerStats returns a new ContainerStats entity and sets in it the given name
func NewContainerStats(name, osType string) *ContainerStats {
return &ContainerStats{
ContainerStatsAttrs: ContainerStatsAttrs{
Name: name,
Windows: (osType == "windows"),
},
}
}
// ContainerStatsWrite renders the context for a list of containers statistics
func ContainerStatsWrite(ctx Context, containerStats []*ContainerStats) error {
render := func(format func(subContext subContext) error) error {
for _, cstats := range containerStats {
cstats.Mu.RLock()
cstatsAttrs := cstats.ContainerStatsAttrs
cstats.Mu.RUnlock()
containerStatsCtx := &containerStatsContext{
s: cstatsAttrs,
}
if err := format(containerStatsCtx); err != nil {
return err
}
}
return nil
}
return ctx.Write(&containerStatsContext{}, render)
}
type containerStatsContext struct {
HeaderContext
s ContainerStatsAttrs
}
func (c *containerStatsContext) Container() string {
c.AddHeader(containerHeader)
return c.s.Name
}
func (c *containerStatsContext) CPUPrec() string {
c.AddHeader(cpuPrecHeader)
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
}
func (c *containerStatsContext) MemUsage() string {
c.AddHeader(memUseHeader)
if !c.s.Windows {
return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
}
return fmt.Sprintf("-- / --")
}
func (c *containerStatsContext) MemPrec() string {
header := memPrecHeader
if c.s.Windows {
header = winMemPrecHeader
}
c.AddHeader(header)
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
}
func (c *containerStatsContext) NetIO() string {
c.AddHeader(netIOHeader)
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
}
func (c *containerStatsContext) BlockIO() string {
c.AddHeader(blockIOHeader)
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
}
func (c *containerStatsContext) PIDs() string {
c.AddHeader(pidsHeader)
if !c.s.Windows {
return fmt.Sprintf("%d", c.s.PidsCurrent)
}
return fmt.Sprintf("-")
}