github.com/olljanat/moby@v1.13.1/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 OSType string 41 } 42 43 // ContainerStats represents an entity to store containers statistics synchronously 44 type ContainerStats struct { 45 mutex sync.Mutex 46 StatsEntry 47 err error 48 } 49 50 // GetError returns the container statistics error. 51 // This is used to determine whether the statistics are valid or not 52 func (cs *ContainerStats) GetError() error { 53 cs.mutex.Lock() 54 defer cs.mutex.Unlock() 55 return cs.err 56 } 57 58 // SetErrorAndReset zeroes all the container statistics and store the error. 59 // It is used when receiving time out error during statistics collecting to reduce lock overhead 60 func (cs *ContainerStats) SetErrorAndReset(err error) { 61 cs.mutex.Lock() 62 defer cs.mutex.Unlock() 63 cs.CPUPercentage = 0 64 cs.Memory = 0 65 cs.MemoryPercentage = 0 66 cs.MemoryLimit = 0 67 cs.NetworkRx = 0 68 cs.NetworkTx = 0 69 cs.BlockRead = 0 70 cs.BlockWrite = 0 71 cs.PidsCurrent = 0 72 cs.err = err 73 cs.IsInvalid = true 74 } 75 76 // SetError sets container statistics error 77 func (cs *ContainerStats) SetError(err error) { 78 cs.mutex.Lock() 79 defer cs.mutex.Unlock() 80 cs.err = err 81 if err != nil { 82 cs.IsInvalid = true 83 } 84 } 85 86 // SetStatistics set the container statistics 87 func (cs *ContainerStats) SetStatistics(s StatsEntry) { 88 cs.mutex.Lock() 89 defer cs.mutex.Unlock() 90 s.Container = cs.Container 91 s.OSType = cs.OSType 92 cs.StatsEntry = s 93 } 94 95 // GetStatistics returns container statistics with other meta data such as the container name 96 func (cs *ContainerStats) GetStatistics() StatsEntry { 97 cs.mutex.Lock() 98 defer cs.mutex.Unlock() 99 return cs.StatsEntry 100 } 101 102 // NewStatsFormat returns a format for rendering an CStatsContext 103 func NewStatsFormat(source, osType string) Format { 104 if source == TableFormatKey { 105 if osType == winOSType { 106 return Format(winDefaultStatsTableFormat) 107 } 108 return Format(defaultStatsTableFormat) 109 } 110 return Format(source) 111 } 112 113 // NewContainerStats returns a new ContainerStats entity and sets in it the given name 114 func NewContainerStats(container, osType string) *ContainerStats { 115 return &ContainerStats{ 116 StatsEntry: StatsEntry{Container: container, OSType: osType}, 117 } 118 } 119 120 // ContainerStatsWrite renders the context for a list of containers statistics 121 func ContainerStatsWrite(ctx Context, containerStats []StatsEntry) error { 122 render := func(format func(subContext subContext) error) error { 123 for _, cstats := range containerStats { 124 containerStatsCtx := &containerStatsContext{ 125 s: cstats, 126 } 127 if err := format(containerStatsCtx); err != nil { 128 return err 129 } 130 } 131 return nil 132 } 133 return ctx.Write(&containerStatsContext{}, render) 134 } 135 136 type containerStatsContext struct { 137 HeaderContext 138 s StatsEntry 139 } 140 141 func (c *containerStatsContext) Container() string { 142 c.AddHeader(containerHeader) 143 return c.s.Container 144 } 145 146 func (c *containerStatsContext) Name() string { 147 c.AddHeader(nameHeader) 148 name := c.s.Name[1:] 149 return name 150 } 151 152 func (c *containerStatsContext) ID() string { 153 c.AddHeader(containerIDHeader) 154 return c.s.ID 155 } 156 157 func (c *containerStatsContext) CPUPerc() string { 158 c.AddHeader(cpuPercHeader) 159 if c.s.IsInvalid { 160 return fmt.Sprintf("--") 161 } 162 return fmt.Sprintf("%.2f%%", c.s.CPUPercentage) 163 } 164 165 func (c *containerStatsContext) MemUsage() string { 166 header := memUseHeader 167 if c.s.OSType == winOSType { 168 header = winMemUseHeader 169 } 170 c.AddHeader(header) 171 if c.s.IsInvalid { 172 return fmt.Sprintf("-- / --") 173 } 174 if c.s.OSType == winOSType { 175 return fmt.Sprintf("%s", units.BytesSize(c.s.Memory)) 176 } 177 return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit)) 178 } 179 180 func (c *containerStatsContext) MemPerc() string { 181 header := memPercHeader 182 c.AddHeader(header) 183 if c.s.IsInvalid || c.s.OSType == winOSType { 184 return fmt.Sprintf("--") 185 } 186 return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage) 187 } 188 189 func (c *containerStatsContext) NetIO() string { 190 c.AddHeader(netIOHeader) 191 if c.s.IsInvalid { 192 return fmt.Sprintf("--") 193 } 194 return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3)) 195 } 196 197 func (c *containerStatsContext) BlockIO() string { 198 c.AddHeader(blockIOHeader) 199 if c.s.IsInvalid { 200 return fmt.Sprintf("--") 201 } 202 return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3)) 203 } 204 205 func (c *containerStatsContext) PIDs() string { 206 c.AddHeader(pidsHeader) 207 if c.s.IsInvalid || c.s.OSType == winOSType { 208 return fmt.Sprintf("--") 209 } 210 return fmt.Sprintf("%d", c.s.PidsCurrent) 211 }