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  }