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 }