github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/cli/command/formatter/stats.go (about)

     1  package formatter
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	units "github.com/docker/go-units"
     8  )
     9  
    10  const (
    11  	winOSType                  = "windows"
    12  	defaultStatsTableFormat    = "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
    13  	winDefaultStatsTableFormat = "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
    14  
    15  	containerHeader = "CONTAINER"
    16  	cpuPercHeader   = "CPU %"
    17  	netIOHeader     = "NET I/O"
    18  	blockIOHeader   = "BLOCK I/O"
    19  	memPercHeader   = "MEM %"             // Used only on Linux
    20  	winMemUseHeader = "PRIV WORKING SET"  // Used only on Windows
    21  	memUseHeader    = "MEM USAGE / LIMIT" // Used only on Linux
    22  	pidsHeader      = "PIDS"              // Used only on Linux
    23  )
    24  
    25  // StatsEntry represents represents the statistics data collected from a container
    26  type StatsEntry struct {
    27  	Name             string
    28  	CPUPercentage    float64
    29  	Memory           float64 // On Windows this is the private working set
    30  	MemoryLimit      float64 // Not used on Windows
    31  	MemoryPercentage float64 // Not used on Windows
    32  	NetworkRx        float64
    33  	NetworkTx        float64
    34  	BlockRead        float64
    35  	BlockWrite       float64
    36  	PidsCurrent      uint64 // Not used on Windows
    37  	IsInvalid        bool
    38  	OSType           string
    39  }
    40  
    41  // ContainerStats represents an entity to store containers statistics synchronously
    42  type ContainerStats struct {
    43  	mutex sync.Mutex
    44  	StatsEntry
    45  	err error
    46  }
    47  
    48  // GetError returns the container statistics error.
    49  // This is used to determine whether the statistics are valid or not
    50  func (cs *ContainerStats) GetError() error {
    51  	cs.mutex.Lock()
    52  	defer cs.mutex.Unlock()
    53  	return cs.err
    54  }
    55  
    56  // SetErrorAndReset zeroes all the container statistics and store the error.
    57  // It is used when receiving time out error during statistics collecting to reduce lock overhead
    58  func (cs *ContainerStats) SetErrorAndReset(err error) {
    59  	cs.mutex.Lock()
    60  	defer cs.mutex.Unlock()
    61  	cs.CPUPercentage = 0
    62  	cs.Memory = 0
    63  	cs.MemoryPercentage = 0
    64  	cs.MemoryLimit = 0
    65  	cs.NetworkRx = 0
    66  	cs.NetworkTx = 0
    67  	cs.BlockRead = 0
    68  	cs.BlockWrite = 0
    69  	cs.PidsCurrent = 0
    70  	cs.err = err
    71  	cs.IsInvalid = true
    72  }
    73  
    74  // SetError sets container statistics error
    75  func (cs *ContainerStats) SetError(err error) {
    76  	cs.mutex.Lock()
    77  	defer cs.mutex.Unlock()
    78  	cs.err = err
    79  	if err != nil {
    80  		cs.IsInvalid = true
    81  	}
    82  }
    83  
    84  // SetStatistics set the container statistics
    85  func (cs *ContainerStats) SetStatistics(s StatsEntry) {
    86  	cs.mutex.Lock()
    87  	defer cs.mutex.Unlock()
    88  	s.Name = cs.Name
    89  	s.OSType = cs.OSType
    90  	cs.StatsEntry = s
    91  }
    92  
    93  // GetStatistics returns container statistics with other meta data such as the container name
    94  func (cs *ContainerStats) GetStatistics() StatsEntry {
    95  	cs.mutex.Lock()
    96  	defer cs.mutex.Unlock()
    97  	return cs.StatsEntry
    98  }
    99  
   100  // NewStatsFormat returns a format for rendering an CStatsContext
   101  func NewStatsFormat(source, osType string) Format {
   102  	if source == TableFormatKey {
   103  		if osType == winOSType {
   104  			return Format(winDefaultStatsTableFormat)
   105  		}
   106  		return Format(defaultStatsTableFormat)
   107  	}
   108  	return Format(source)
   109  }
   110  
   111  // NewContainerStats returns a new ContainerStats entity and sets in it the given name
   112  func NewContainerStats(name, osType string) *ContainerStats {
   113  	return &ContainerStats{
   114  		StatsEntry: StatsEntry{Name: name, OSType: osType},
   115  	}
   116  }
   117  
   118  // ContainerStatsWrite renders the context for a list of containers statistics
   119  func ContainerStatsWrite(ctx Context, containerStats []StatsEntry) error {
   120  	render := func(format func(subContext subContext) error) error {
   121  		for _, cstats := range containerStats {
   122  			containerStatsCtx := &containerStatsContext{
   123  				s: cstats,
   124  			}
   125  			if err := format(containerStatsCtx); err != nil {
   126  				return err
   127  			}
   128  		}
   129  		return nil
   130  	}
   131  	return ctx.Write(&containerStatsContext{}, render)
   132  }
   133  
   134  type containerStatsContext struct {
   135  	HeaderContext
   136  	s StatsEntry
   137  }
   138  
   139  func (c *containerStatsContext) Container() string {
   140  	c.AddHeader(containerHeader)
   141  	return c.s.Name
   142  }
   143  
   144  func (c *containerStatsContext) CPUPerc() string {
   145  	c.AddHeader(cpuPercHeader)
   146  	if c.s.IsInvalid {
   147  		return fmt.Sprintf("--")
   148  	}
   149  	return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
   150  }
   151  
   152  func (c *containerStatsContext) MemUsage() string {
   153  	header := memUseHeader
   154  	if c.s.OSType == winOSType {
   155  		header = winMemUseHeader
   156  	}
   157  	c.AddHeader(header)
   158  	if c.s.IsInvalid {
   159  		return fmt.Sprintf("-- / --")
   160  	}
   161  	if c.s.OSType == winOSType {
   162  		return fmt.Sprintf("%s", units.BytesSize(c.s.Memory))
   163  	}
   164  	return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
   165  }
   166  
   167  func (c *containerStatsContext) MemPerc() string {
   168  	header := memPercHeader
   169  	c.AddHeader(header)
   170  	if c.s.IsInvalid || c.s.OSType == winOSType {
   171  		return fmt.Sprintf("--")
   172  	}
   173  	return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
   174  }
   175  
   176  func (c *containerStatsContext) NetIO() string {
   177  	c.AddHeader(netIOHeader)
   178  	if c.s.IsInvalid {
   179  		return fmt.Sprintf("--")
   180  	}
   181  	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
   182  }
   183  
   184  func (c *containerStatsContext) BlockIO() string {
   185  	c.AddHeader(blockIOHeader)
   186  	if c.s.IsInvalid {
   187  		return fmt.Sprintf("--")
   188  	}
   189  	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
   190  }
   191  
   192  func (c *containerStatsContext) PIDs() string {
   193  	c.AddHeader(pidsHeader)
   194  	if c.s.IsInvalid || c.s.OSType == winOSType {
   195  		return fmt.Sprintf("--")
   196  	}
   197  	return fmt.Sprintf("%d", c.s.PidsCurrent)
   198  }