github.com/containerd/nerdctl@v1.7.7/pkg/statsutil/stats.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package statsutil 18 19 import ( 20 "fmt" 21 "strconv" 22 "sync" 23 "time" 24 25 units "github.com/docker/go-units" 26 ) 27 28 // StatsEntry represents the statistics data collected from a container 29 type StatsEntry struct { 30 Container string 31 Name string 32 ID string 33 CPUPercentage float64 34 Memory float64 35 MemoryLimit float64 36 MemoryPercentage float64 37 NetworkRx float64 38 NetworkTx float64 39 BlockRead float64 40 BlockWrite float64 41 PidsCurrent uint64 42 IsInvalid bool 43 } 44 45 // FormattedStatsEntry represents a formatted StatsEntry 46 type FormattedStatsEntry struct { 47 Name string 48 ID string 49 CPUPerc string 50 MemUsage string 51 MemPerc string 52 NetIO string 53 BlockIO string 54 PIDs string 55 } 56 57 // Stats represents an entity to store containers statistics synchronously 58 type Stats struct { 59 mutex sync.RWMutex 60 StatsEntry 61 err error 62 } 63 64 // ContainerStats represents the runtime container stats 65 type ContainerStats struct { 66 Time time.Time 67 CgroupCPU, Cgroup2CPU uint64 68 CgroupSystem, Cgroup2System uint64 69 } 70 71 // NewStats is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L113-L116 72 func NewStats(container string) *Stats { 73 return &Stats{StatsEntry: StatsEntry{Container: container}} 74 } 75 76 // SetStatistics is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L87-L93 77 func (cs *Stats) SetStatistics(s StatsEntry) { 78 cs.mutex.Lock() 79 defer cs.mutex.Unlock() 80 s.Container = cs.Container 81 cs.StatsEntry = s 82 } 83 84 // GetStatistics is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L95-L100 85 func (cs *Stats) GetStatistics() StatsEntry { 86 cs.mutex.Lock() 87 defer cs.mutex.Unlock() 88 return cs.StatsEntry 89 } 90 91 // GetError is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L51-L57 92 func (cs *Stats) GetError() error { 93 cs.mutex.Lock() 94 defer cs.mutex.Unlock() 95 return cs.err 96 } 97 98 // SetErrorAndReset is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L59-L75 99 func (cs *Stats) SetErrorAndReset(err error) { 100 cs.mutex.Lock() 101 defer cs.mutex.Unlock() 102 cs.CPUPercentage = 0 103 cs.Memory = 0 104 cs.MemoryPercentage = 0 105 cs.MemoryLimit = 0 106 cs.NetworkRx = 0 107 cs.NetworkTx = 0 108 cs.BlockRead = 0 109 cs.BlockWrite = 0 110 cs.PidsCurrent = 0 111 cs.err = err 112 cs.IsInvalid = true 113 } 114 115 // SetError is from https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/command/container/formatter_stats.go#L77-L85 116 func (cs *Stats) SetError(err error) { 117 cs.mutex.Lock() 118 defer cs.mutex.Unlock() 119 cs.err = err 120 if err != nil { 121 cs.IsInvalid = true 122 } 123 } 124 125 func calculateMemPercent(limit float64, usedNo float64) float64 { 126 // Limit will never be 0 unless the container is not running and we haven't 127 // got any data from cgroup 128 if limit != 0 { 129 return usedNo / limit * 100.0 130 } 131 return 0 132 } 133 134 // Rendering a FormattedStatsEntry from StatsEntry 135 func RenderEntry(in *StatsEntry, noTrunc bool) FormattedStatsEntry { 136 return FormattedStatsEntry{ 137 Name: in.EntryName(), 138 ID: in.EntryID(noTrunc), 139 CPUPerc: in.CPUPerc(), 140 MemUsage: in.MemUsage(), 141 MemPerc: in.MemPerc(), 142 NetIO: in.NetIO(), 143 BlockIO: in.BlockIO(), 144 PIDs: in.PIDs(), 145 } 146 } 147 148 /* 149 a set of functions to format container stats 150 */ 151 func (s *StatsEntry) EntryName() string { 152 if len(s.Name) > 1 { 153 if len(s.Name) > 12 { 154 return s.Name[:12] 155 } 156 return s.Name 157 } 158 return "--" 159 } 160 161 func (s *StatsEntry) EntryID(noTrunc bool) string { 162 if !noTrunc { 163 if len(s.ID) > 12 { 164 return s.ID[:12] 165 } 166 } 167 return s.ID 168 } 169 170 func (s *StatsEntry) CPUPerc() string { 171 if s.IsInvalid { 172 return "--" 173 } 174 return fmt.Sprintf("%.2f%%", s.CPUPercentage) 175 } 176 177 func (s *StatsEntry) MemUsage() string { 178 if s.IsInvalid { 179 return "-- / --" 180 } 181 return fmt.Sprintf("%s / %s", units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit)) 182 } 183 184 func (s *StatsEntry) MemPerc() string { 185 if s.IsInvalid { 186 return "--" 187 } 188 return fmt.Sprintf("%.2f%%", s.MemoryPercentage) 189 } 190 191 func (s *StatsEntry) NetIO() string { 192 if s.IsInvalid { 193 return "--" 194 } 195 return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(s.NetworkRx, 3), units.HumanSizeWithPrecision(s.NetworkTx, 3)) 196 } 197 198 func (s *StatsEntry) BlockIO() string { 199 if s.IsInvalid { 200 return "--" 201 } 202 return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(s.BlockRead, 3), units.HumanSizeWithPrecision(s.BlockWrite, 3)) 203 } 204 205 func (s *StatsEntry) PIDs() string { 206 if s.IsInvalid { 207 return "--" 208 } 209 return strconv.FormatUint(s.PidsCurrent, 10) 210 }