github.com/matrixorigin/matrixone@v1.2.0/pkg/util/metric/m_hardware.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metric 16 17 import ( 18 "context" 19 "strings" 20 21 "github.com/matrixorigin/matrixone/pkg/common/moerr" 22 "github.com/matrixorigin/matrixone/pkg/logutil" 23 prom "github.com/prometheus/client_golang/prometheus" 24 "github.com/shirou/gopsutil/v3/cpu" 25 "github.com/shirou/gopsutil/v3/disk" 26 "github.com/shirou/gopsutil/v3/mem" 27 "github.com/shirou/gopsutil/v3/net" 28 ) 29 30 var hardwareStatsCollector = newBatchStatsCollector( 31 cpuTotal{}, 32 cpuPercent{}, 33 memUsed{}, 34 memAvail{}, 35 diskR{}, 36 diskW{}, 37 netR{}, 38 netW{}, 39 ) 40 41 var logicalCore int 42 43 func init() { 44 logicalCore, _ = cpu.Counts(true) 45 } 46 47 type cpuTotal struct{} 48 49 func (c cpuTotal) Desc() *prom.Desc { 50 return prom.NewDesc( 51 "sys_cpu_seconds_total", 52 "System CPU time spent in seconds, normalized by number of cores", 53 nil, sysTenantID, 54 ) 55 } 56 57 func (c cpuTotal) Metric(ctx context.Context, _ *statCaches) (prom.Metric, error) { 58 cpus, _ := cpu.Times(false) 59 if len(cpus) == 0 { 60 return nil, moerr.NewInternalError(ctx, "empty cpu times") 61 } 62 v := (CPUTotalTime(cpus[0]) - cpus[0].Idle) / float64(logicalCore) 63 return prom.MustNewConstMetric(c.Desc(), prom.CounterValue, v), nil 64 } 65 66 type cpuPercent struct{} 67 68 func (c cpuPercent) Desc() *prom.Desc { 69 return prom.NewDesc( 70 "sys_cpu_combined_percent", 71 "System CPU busy percentage, average among all logical cores", 72 nil, sysTenantID, 73 ) 74 } 75 76 func (c cpuPercent) Metric(ctx context.Context, _ *statCaches) (prom.Metric, error) { 77 percents, err := cpu.Percent(0, false) 78 if err != nil { 79 return nil, err 80 } 81 if len(percents) == 0 { 82 return nil, moerr.NewInternalError(ctx, "empty cpu percents") 83 } 84 85 return prom.MustNewConstMetric(c.Desc(), prom.GaugeValue, percents[0]), nil 86 } 87 88 // getMemStats get common data for memory metric 89 func getMemStats() any /* *mem.VirtualMemoryStat */ { 90 if memstats, err := mem.VirtualMemory(); err == nil { 91 return memstats 92 } else { 93 logutil.Warnf("[Metric] failed to get VirtualMemory, %v", err) 94 return nil 95 } 96 } 97 98 // memTotal = /proc/meminfo.{MemFree + Cached} 99 type memUsed struct{} 100 101 func (m memUsed) Desc() *prom.Desc { 102 return prom.NewDesc( 103 "sys_memory_used", 104 "System memory used in bytes", 105 nil, sysTenantID, 106 ) 107 } 108 109 func (m memUsed) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 110 val := s.getOrInsert(cacheKeyMemStats, getMemStats) 111 if val == nil { 112 return nil, moerr.NewInternalError(ctx, "empty available memomry") 113 } 114 memostats := val.(*mem.VirtualMemoryStat) 115 return prom.MustNewConstMetric(m.Desc(), prom.GaugeValue, float64(memostats.Used)), nil 116 } 117 118 // memAail = /proc/meminfo.MemAvailable 119 type memAvail struct{} 120 121 func (m memAvail) Desc() *prom.Desc { 122 return prom.NewDesc( 123 "sys_memory_available", 124 "System memory available in bytes", 125 nil, sysTenantID, 126 ) 127 } 128 129 func (m memAvail) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 130 val := s.getOrInsert(cacheKeyMemStats, getMemStats) 131 if val == nil { 132 return nil, moerr.NewInternalError(ctx, "empty available memomry") 133 } 134 memostats := val.(*mem.VirtualMemoryStat) 135 return prom.MustNewConstMetric(m.Desc(), prom.GaugeValue, float64(memostats.Available)), nil 136 } 137 138 func getDiskStats() any { 139 if diskStats, err := disk.IOCounters(); err == nil { 140 var total disk.IOCountersStat 141 for _, v := range diskStats { 142 total.ReadBytes += v.ReadBytes 143 total.WriteBytes += v.WriteBytes 144 } 145 return &total 146 } else { 147 logutil.Warnf("[Metric] failed to get DiskIOCounters, %v", err) 148 return nil 149 } 150 } 151 152 // if we have window function, we can just use NewConstMetric 153 var diskRead = NewCounter(prom.CounterOpts{ 154 Subsystem: "sys", 155 Name: "disk_read_bytes", 156 Help: "Total read bytes of all disks", 157 ConstLabels: sysTenantID, 158 }) 159 160 var diskWrite = NewCounter(prom.CounterOpts{ 161 Subsystem: "sys", 162 Name: "disk_write_bytes", 163 Help: "Total write bytes of all disks", 164 ConstLabels: sysTenantID, 165 }) 166 167 type diskR struct{} 168 169 func (d diskR) Desc() *prom.Desc { 170 return diskRead.Desc() 171 } 172 173 func (d diskR) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 174 val := s.getOrInsert(cacheKeyDiskIO, getDiskStats) 175 if val == nil { 176 return nil, moerr.NewInternalError(ctx, "empty available disk stats") 177 } 178 memostats := val.(*disk.IOCountersStat) 179 diskRead.Set(memostats.ReadBytes) 180 return diskRead, nil 181 } 182 183 type diskW struct{} 184 185 func (d diskW) Desc() *prom.Desc { 186 return diskWrite.Desc() 187 } 188 189 func (d diskW) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 190 val := s.getOrInsert(cacheKeyDiskIO, getDiskStats) 191 if val == nil { 192 return nil, moerr.NewInternalError(ctx, "empty available disk stats") 193 } 194 memostats := val.(*disk.IOCountersStat) 195 diskWrite.Set(memostats.WriteBytes) 196 return diskWrite, nil 197 } 198 199 func getNetStats() any { 200 if netStats, err := net.IOCounters(true); err == nil { 201 var total net.IOCountersStat 202 for _, v := range netStats { 203 if strings.HasPrefix(v.Name, "lo") { 204 continue 205 } 206 total.BytesRecv += v.BytesRecv 207 total.BytesSent += v.BytesSent 208 } 209 return &total 210 } else { 211 logutil.Warnf("[Metric] failed to get DiskIOCounters, %v", err) 212 return nil 213 } 214 } 215 216 var netRead = NewCounter(prom.CounterOpts{ 217 Subsystem: "sys", 218 Name: "net_recv_bytes", 219 Help: "Total recv bytes of all nic (expect lo)", 220 ConstLabels: sysTenantID, 221 }) 222 223 var netWrite = NewCounter(prom.CounterOpts{ 224 Subsystem: "sys", 225 Name: "net_sent_bytes", 226 Help: "Total sent bytes of all nic (expect lo)", 227 ConstLabels: sysTenantID, 228 }) 229 230 type netR struct{} 231 232 func (d netR) Desc() *prom.Desc { 233 return netRead.Desc() 234 } 235 236 func (d netR) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 237 val := s.getOrInsert(cacheKeyNetIO, getNetStats) 238 if val == nil { 239 return nil, moerr.NewInternalError(ctx, "empty available net stats") 240 } 241 memostats := val.(*net.IOCountersStat) 242 netRead.Set(memostats.BytesRecv) 243 return netRead, nil 244 } 245 246 type netW struct{} 247 248 func (d netW) Desc() *prom.Desc { 249 return netWrite.Desc() 250 } 251 252 func (d netW) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 253 val := s.getOrInsert(cacheKeyNetIO, getNetStats) 254 if val == nil { 255 return nil, moerr.NewInternalError(ctx, "empty available net stats") 256 } 257 memostats := val.(*net.IOCountersStat) 258 netWrite.Set(memostats.BytesSent) 259 return netWrite, nil 260 }