123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- package formatter
- import (
- "fmt"
- "sync"
- units "github.com/docker/go-units"
- )
- const (
- winOSType = "windows"
- defaultStatsTableFormat = "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
- winDefaultStatsTableFormat = "table {{.Container}}\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
- OSType string
- }
- // 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
- s.OSType = cs.OSType
- 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, osType string) *ContainerStats {
- return &ContainerStats{
- StatsEntry: StatsEntry{Container: container, OSType: osType},
- }
- }
- // ContainerStatsWrite renders the context for a list of containers statistics
- func ContainerStatsWrite(ctx Context, containerStats []StatsEntry) error {
- render := func(format func(subContext subContext) error) error {
- for _, cstats := range containerStats {
- containerStatsCtx := &containerStatsContext{
- s: cstats,
- }
- if err := format(containerStatsCtx); err != nil {
- return err
- }
- }
- return nil
- }
- return ctx.Write(&containerStatsContext{}, render)
- }
- type containerStatsContext struct {
- HeaderContext
- s StatsEntry
- }
- func (c *containerStatsContext) Container() string {
- c.AddHeader(containerHeader)
- return c.s.Container
- }
- func (c *containerStatsContext) Name() string {
- c.AddHeader(nameHeader)
- name := c.s.Name[1:]
- return name
- }
- func (c *containerStatsContext) ID() string {
- c.AddHeader(containerIDHeader)
- return c.s.ID
- }
- func (c *containerStatsContext) CPUPerc() string {
- c.AddHeader(cpuPercHeader)
- if c.s.IsInvalid {
- return fmt.Sprintf("--")
- }
- return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
- }
- func (c *containerStatsContext) MemUsage() string {
- header := memUseHeader
- if c.s.OSType == winOSType {
- header = winMemUseHeader
- }
- c.AddHeader(header)
- if c.s.IsInvalid {
- return fmt.Sprintf("-- / --")
- }
- if c.s.OSType == winOSType {
- return fmt.Sprintf("%s", 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 {
- header := memPercHeader
- c.AddHeader(header)
- if c.s.IsInvalid || c.s.OSType == winOSType {
- return fmt.Sprintf("--")
- }
- return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
- }
- func (c *containerStatsContext) NetIO() string {
- c.AddHeader(netIOHeader)
- 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 {
- c.AddHeader(blockIOHeader)
- 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 {
- c.AddHeader(pidsHeader)
- if c.s.IsInvalid || c.s.OSType == winOSType {
- return fmt.Sprintf("--")
- }
- return fmt.Sprintf("%d", c.s.PidsCurrent)
- }
|