github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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  	Container        string
    28  	Name             string
    29  	ID               string
    30  	CPUPercentage    float64
    31  	Memory           float64 // On Windows this is the private working set
    32  	MemoryLimit      float64 // Not used on Windows
    33  	MemoryPercentage float64 // Not used on Windows
    34  	NetworkRx        float64
    35  	NetworkTx        float64
    36  	BlockRead        float64
    37  	BlockWrite       float64
    38  	PidsCurrent      uint64 // Not used on Windows
    39  	IsInvalid        bool
    40  }
    41  
    42  // ContainerStats represents an entity to store containers statistics synchronously
    43  type ContainerStats struct {
    44  	mutex sync.Mutex
    45  	StatsEntry
    46  	err error
    47  }
    48  
    49  // GetError returns the container statistics error.
    50  // This is used to determine whether the statistics are valid or not
    51  func (cs *ContainerStats) GetError() error {
    52  	cs.mutex.Lock()
    53  	defer cs.mutex.Unlock()
    54  	return cs.err
    55  }
    56  
    57  // SetErrorAndReset zeroes all the container statistics and store the error.
    58  // It is used when receiving time out error during statistics collecting to reduce lock overhead
    59  func (cs *ContainerStats) SetErrorAndReset(err error) {
    60  	cs.mutex.Lock()
    61  	defer cs.mutex.Unlock()
    62  	cs.CPUPercentage = 0
    63  	cs.Memory = 0
    64  	cs.MemoryPercentage = 0
    65  	cs.MemoryLimit = 0
    66  	cs.NetworkRx = 0
    67  	cs.NetworkTx = 0
    68  	cs.BlockRead = 0
    69  	cs.BlockWrite = 0
    70  	cs.PidsCurrent = 0
    71  	cs.err = err
    72  	cs.IsInvalid = true
    73  }
    74  
    75  // SetError sets container statistics error
    76  func (cs *ContainerStats) SetError(err error) {
    77  	cs.mutex.Lock()
    78  	defer cs.mutex.Unlock()
    79  	cs.err = err
    80  	if err != nil {
    81  		cs.IsInvalid = true
    82  	}
    83  }
    84  
    85  // SetStatistics set the container statistics
    86  func (cs *ContainerStats) SetStatistics(s StatsEntry) {
    87  	cs.mutex.Lock()
    88  	defer cs.mutex.Unlock()
    89  	s.Container = cs.Container
    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(container, osType string) *ContainerStats {
   113  	return &ContainerStats{
   114  		StatsEntry: StatsEntry{Container: container},
   115  	}
   116  }
   117  
   118  // ContainerStatsWrite renders the context for a list of containers statistics
   119  func ContainerStatsWrite(ctx Context, containerStats []StatsEntry, osType string) error {
   120  	render := func(format func(subContext subContext) error) error {
   121  		for _, cstats := range containerStats {
   122  			containerStatsCtx := &containerStatsContext{
   123  				s:  cstats,
   124  				os: osType,
   125  			}
   126  			if err := format(containerStatsCtx); err != nil {
   127  				return err
   128  			}
   129  		}
   130  		return nil
   131  	}
   132  	return ctx.Write(&containerStatsContext{os: osType}, render)
   133  }
   134  
   135  type containerStatsContext struct {
   136  	HeaderContext
   137  	s  StatsEntry
   138  	os string
   139  }
   140  
   141  func (c *containerStatsContext) MarshalJSON() ([]byte, error) {
   142  	return marshalJSON(c)
   143  }
   144  
   145  func (c *containerStatsContext) Container() string {
   146  	c.AddHeader(containerHeader)
   147  	return c.s.Container
   148  }
   149  
   150  func (c *containerStatsContext) Name() string {
   151  	c.AddHeader(nameHeader)
   152  	if len(c.s.Name) > 1 {
   153  		return c.s.Name[1:]
   154  	}
   155  	return "--"
   156  }
   157  
   158  func (c *containerStatsContext) ID() string {
   159  	c.AddHeader(containerIDHeader)
   160  	return c.s.ID
   161  }
   162  
   163  func (c *containerStatsContext) CPUPerc() string {
   164  	c.AddHeader(cpuPercHeader)
   165  	if c.s.IsInvalid {
   166  		return fmt.Sprintf("--")
   167  	}
   168  	return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
   169  }
   170  
   171  func (c *containerStatsContext) MemUsage() string {
   172  	header := memUseHeader
   173  	if c.os == winOSType {
   174  		header = winMemUseHeader
   175  	}
   176  	c.AddHeader(header)
   177  	if c.s.IsInvalid {
   178  		return fmt.Sprintf("-- / --")
   179  	}
   180  	if c.os == winOSType {
   181  		return fmt.Sprintf("%s", units.BytesSize(c.s.Memory))
   182  	}
   183  	return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
   184  }
   185  
   186  func (c *containerStatsContext) MemPerc() string {
   187  	header := memPercHeader
   188  	c.AddHeader(header)
   189  	if c.s.IsInvalid || c.os == winOSType {
   190  		return fmt.Sprintf("--")
   191  	}
   192  	return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
   193  }
   194  
   195  func (c *containerStatsContext) NetIO() string {
   196  	c.AddHeader(netIOHeader)
   197  	if c.s.IsInvalid {
   198  		return fmt.Sprintf("--")
   199  	}
   200  	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
   201  }
   202  
   203  func (c *containerStatsContext) BlockIO() string {
   204  	c.AddHeader(blockIOHeader)
   205  	if c.s.IsInvalid {
   206  		return fmt.Sprintf("--")
   207  	}
   208  	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
   209  }
   210  
   211  func (c *containerStatsContext) PIDs() string {
   212  	c.AddHeader(pidsHeader)
   213  	if c.s.IsInvalid || c.os == winOSType {
   214  		return fmt.Sprintf("--")
   215  	}
   216  	return fmt.Sprintf("%d", c.s.PidsCurrent)
   217  }