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 }