This patch adds a `--no-trunc` option to `docker container stats`;
With this patch applied, the default output is:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b95a83497c91 awesome_brattain 0.28% 5.629MiB / 1.952GiB 0.28% 916B / 0B 147kB / 0B 9
67b2525d8ad1 foobar 0.00% 1.727MiB / 1.952GiB 0.09% 2.48kB / 0B 4.11MB / 0B 2
e5c383697914 test-1951.1.kay7x1lh1twk9c0oig50sd5tr 0.00% 196KiB / 1.952GiB 0.01% 71.2kB / 0B 770kB / 0B 1
4bda148efbc0 random.1.vnc8on831idyr42slu578u3cr 0.00% 1.672MiB / 1.952GiB 0.08% 110kB / 0B 578kB / 0B 2
84e3deaa45b2 registry 0.01% 3.402MiB / 1.952GiB 0.17% 127kB / 378B 233kB / 0B 10
2ed915778ceb foo.1.lsmxrefn5yp9c9ijz1hzgdq4u 0.00% 1.727MiB / 1.952GiB 0.09% 166kB / 7.76kB 614kB / 0B 2
Addin the `--no-trunc` option, changes the output to:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b95a83497c9161c9b444e3d70e1a9dfba0c1840d41720e146a95a08ebf938afc awesome_brattain 0.25% 5.75MiB / 1.952GiB 0.29% 648B / 0B 147kB / 0B 10
67b2525d8ad10bb236a49960e93c09993b0baabeef12c2d46cd5f4fbb6f4808c foobar 0.00% 1.727MiB / 1.952GiB 0.09% 2.35kB / 0B 4.11MB / 0B 2
e5c383697914b98b10cbbc9d0bd324b7b927099ac584f031057b8208d2fba9b1 test-1951.1.kay7x1lh1twk9c0oig50sd5tr 0.00% 196KiB / 1.952GiB 0.01% 71.1kB / 0B 770kB / 0B 1
4bda148efbc006b0063373c3678083159af89f8cc83a6a28def14cb0dd171f70 random.1.vnc8on831idyr42slu578u3cr 0.00% 1.672MiB / 1.952GiB 0.08% 110kB / 0B 578kB / 0B 2
84e3deaa45b2fc363e06167df9b90ab59f88d4f101e3f9b8df03a62a8f6783e1 registry 0.00% 3.387MiB / 1.952GiB 0.17% 127kB / 378B 233kB / 0B 10
2ed915778cebddf9ec69263a75cfdcf00962a5198d94d42cda75d5cd45bb82f2 foo.1.lsmxrefn5yp9c9ijz1hzgdq4u 0.00% 1.727MiB / 1.952GiB 0.09% 166kB / 7.76kB 614kB / 0B 2
Which is the same as the default before this patch was applied.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
225 lines
5.9 KiB
Go
225 lines
5.9 KiB
Go
package formatter
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/docker/docker/pkg/stringid"
|
|
units "github.com/docker/go-units"
|
|
)
|
|
|
|
const (
|
|
winOSType = "windows"
|
|
defaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
|
|
winDefaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
|
|
|
|
containerHeader = "CONTAINER"
|
|
cpuPercHeader = "CPU %"
|
|
netIOHeader = "NET I/O"
|
|
blockIOHeader = "BLOCK I/O"
|
|
memPercHeader = "MEM %" // Used only on Linux
|
|
winMemUseHeader = "PRIV WORKING SET" // Used only on Windows
|
|
memUseHeader = "MEM USAGE / LIMIT" // Used only on Linux
|
|
pidsHeader = "PIDS" // Used only on Linux
|
|
)
|
|
|
|
// StatsEntry represents represents the statistics data collected from a container
|
|
type StatsEntry struct {
|
|
Container string
|
|
Name string
|
|
ID 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
|
|
IsInvalid bool
|
|
}
|
|
|
|
// ContainerStats represents an entity to store containers statistics synchronously
|
|
type ContainerStats struct {
|
|
mutex sync.Mutex
|
|
StatsEntry
|
|
err error
|
|
}
|
|
|
|
// GetError returns the container statistics error.
|
|
// This is used to determine whether the statistics are valid or not
|
|
func (cs *ContainerStats) GetError() error {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
return cs.err
|
|
}
|
|
|
|
// SetErrorAndReset zeroes all the container statistics and store the error.
|
|
// It is used when receiving time out error during statistics collecting to reduce lock overhead
|
|
func (cs *ContainerStats) SetErrorAndReset(err error) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
cs.CPUPercentage = 0
|
|
cs.Memory = 0
|
|
cs.MemoryPercentage = 0
|
|
cs.MemoryLimit = 0
|
|
cs.NetworkRx = 0
|
|
cs.NetworkTx = 0
|
|
cs.BlockRead = 0
|
|
cs.BlockWrite = 0
|
|
cs.PidsCurrent = 0
|
|
cs.err = err
|
|
cs.IsInvalid = true
|
|
}
|
|
|
|
// SetError sets container statistics error
|
|
func (cs *ContainerStats) SetError(err error) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
cs.err = err
|
|
if err != nil {
|
|
cs.IsInvalid = true
|
|
}
|
|
}
|
|
|
|
// SetStatistics set the container statistics
|
|
func (cs *ContainerStats) SetStatistics(s StatsEntry) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
s.Container = cs.Container
|
|
cs.StatsEntry = s
|
|
}
|
|
|
|
// GetStatistics returns container statistics with other meta data such as the container name
|
|
func (cs *ContainerStats) GetStatistics() StatsEntry {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
return cs.StatsEntry
|
|
}
|
|
|
|
// NewStatsFormat returns a format for rendering an CStatsContext
|
|
func NewStatsFormat(source, osType string) Format {
|
|
if source == TableFormatKey {
|
|
if osType == winOSType {
|
|
return Format(winDefaultStatsTableFormat)
|
|
}
|
|
return Format(defaultStatsTableFormat)
|
|
}
|
|
return Format(source)
|
|
}
|
|
|
|
// NewContainerStats returns a new ContainerStats entity and sets in it the given name
|
|
func NewContainerStats(container string) *ContainerStats {
|
|
return &ContainerStats{StatsEntry: StatsEntry{Container: container}}
|
|
}
|
|
|
|
// ContainerStatsWrite renders the context for a list of containers statistics
|
|
func ContainerStatsWrite(ctx Context, containerStats []StatsEntry, osType string, trunc bool) error {
|
|
render := func(format func(subContext subContext) error) error {
|
|
for _, cstats := range containerStats {
|
|
containerStatsCtx := &containerStatsContext{
|
|
s: cstats,
|
|
os: osType,
|
|
trunc: trunc,
|
|
}
|
|
if err := format(containerStatsCtx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
memUsage := memUseHeader
|
|
if osType == winOSType {
|
|
memUsage = winMemUseHeader
|
|
}
|
|
containerStatsCtx := containerStatsContext{}
|
|
containerStatsCtx.header = map[string]string{
|
|
"Container": containerHeader,
|
|
"Name": nameHeader,
|
|
"ID": containerIDHeader,
|
|
"CPUPerc": cpuPercHeader,
|
|
"MemUsage": memUsage,
|
|
"MemPerc": memPercHeader,
|
|
"NetIO": netIOHeader,
|
|
"BlockIO": blockIOHeader,
|
|
"PIDs": pidsHeader,
|
|
}
|
|
containerStatsCtx.os = osType
|
|
return ctx.Write(&containerStatsCtx, render)
|
|
}
|
|
|
|
type containerStatsContext struct {
|
|
HeaderContext
|
|
s StatsEntry
|
|
os string
|
|
trunc bool
|
|
}
|
|
|
|
func (c *containerStatsContext) MarshalJSON() ([]byte, error) {
|
|
return marshalJSON(c)
|
|
}
|
|
|
|
func (c *containerStatsContext) Container() string {
|
|
return c.s.Container
|
|
}
|
|
|
|
func (c *containerStatsContext) Name() string {
|
|
if len(c.s.Name) > 1 {
|
|
return c.s.Name[1:]
|
|
}
|
|
return "--"
|
|
}
|
|
|
|
func (c *containerStatsContext) ID() string {
|
|
if c.trunc {
|
|
return stringid.TruncateID(c.s.ID)
|
|
}
|
|
return c.s.ID
|
|
}
|
|
|
|
func (c *containerStatsContext) CPUPerc() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
|
|
}
|
|
|
|
func (c *containerStatsContext) MemUsage() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("-- / --")
|
|
}
|
|
if c.os == winOSType {
|
|
return units.BytesSize(c.s.Memory)
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
|
|
}
|
|
|
|
func (c *containerStatsContext) MemPerc() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
|
|
}
|
|
|
|
func (c *containerStatsContext) NetIO() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
|
|
}
|
|
|
|
func (c *containerStatsContext) BlockIO() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
|
|
}
|
|
|
|
func (c *containerStatsContext) PIDs() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%d", c.s.PidsCurrent)
|
|
}
|