github.com/matrixorigin/matrixone@v1.2.0/pkg/util/metric/m_process.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 "os" 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/process" 25 ) 26 27 // ProcessCollector collect following information about the current MO process: 28 // 29 // - CPUTime (Sys + User + Iowait) in seconds (percent for now) 30 // - open fds & max fds (not available on MacOS) 31 // - virtual_resident_mem_bytes 32 33 var processCollector = newBatchStatsCollector(procCpuPercent{}, procMemUsage{}, procOpenFds{}, procCpuTotal{}) 34 35 var pid = int32(os.Getpid()) 36 37 var getProcess = func() any { 38 if proc, err := process.NewProcess(pid); err == nil { 39 return proc 40 } else { 41 logutil.Warnf("[Metric] failed to get current process %d, %v", pid, err) 42 return nil 43 } 44 } 45 46 // this percent may exceeds 100% on multicore platform 47 type procCpuPercent struct{} 48 49 func (c procCpuPercent) Desc() *prom.Desc { 50 return prom.NewDesc( 51 "process_cpu_percent", 52 "Process CPU busy percentage", 53 nil, sysTenantID, 54 ) 55 } 56 57 func (c procCpuPercent) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 58 val := s.getOrInsert(cacheKeyProcess, getProcess) 59 if val == nil { 60 return nil, moerr.NewInternalError(ctx, "empty process") 61 } 62 proc := val.(*process.Process) 63 64 // Percent use cpuStats.Total because cpuStats in process has no Idel field 65 if percent, err := proc.CPUPercent(); err != nil { 66 return nil, err 67 } else { 68 return prom.MustNewConstMetric(c.Desc(), prom.GaugeValue, percent), nil 69 } 70 } 71 72 type procMemUsage struct{} 73 74 func (c procMemUsage) Desc() *prom.Desc { 75 return prom.NewDesc( 76 "process_resident_memory_bytes", 77 "Resident memory size in bytes.", 78 nil, sysTenantID, 79 ) 80 } 81 82 func (c procMemUsage) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 83 val := s.getOrInsert(cacheKeyProcess, getProcess) 84 if val == nil { 85 return nil, moerr.NewInternalError(ctx, "empty process") 86 } 87 proc := val.(*process.Process) 88 if mem, err := proc.MemoryInfo(); err != nil { 89 return nil, err 90 } else { 91 return prom.MustNewConstMetric(c.Desc(), prom.GaugeValue, float64(mem.RSS)), nil 92 } 93 } 94 95 type procOpenFds struct{} 96 97 func (c procOpenFds) Desc() *prom.Desc { 98 return prom.NewDesc( 99 "process_open_fds", 100 "Number of open file descriptors.", 101 nil, sysTenantID, 102 ) 103 } 104 105 func (c procOpenFds) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 106 val := s.getOrInsert(cacheKeyProcess, getProcess) 107 if val == nil { 108 return nil, moerr.NewInternalError(ctx, "empty process") 109 } 110 proc := val.(*process.Process) 111 if fds, err := proc.NumFDs(); err != nil { 112 return nil, err 113 } else { 114 return prom.MustNewConstMetric(c.Desc(), prom.GaugeValue, float64(fds)), nil 115 } 116 } 117 118 // procFdsLimit means open file limit 119 // 120 // Deprecated 121 type procFdsLimit struct{} 122 123 func (c procFdsLimit) Desc() *prom.Desc { 124 return prom.NewDesc( 125 "process_max_fds", 126 "Maximum number of open file descriptors.", 127 nil, sysTenantID, 128 ) 129 } 130 131 func (c procFdsLimit) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 132 val := s.getOrInsert(cacheKeyProcess, getProcess) 133 if val == nil { 134 return nil, moerr.NewInternalError(ctx, "empty process") 135 } 136 proc := val.(*process.Process) 137 if limits, err := proc.Rlimit(); err != nil { 138 return nil, err 139 } else { 140 for _, limit := range limits { 141 if limit.Resource == process.RLIMIT_NOFILE { 142 return prom.MustNewConstMetric(c.Desc(), prom.GaugeValue, float64(limit.Soft)), nil 143 } 144 } 145 return nil, moerr.NewInternalError(ctx, "empty limit") 146 } 147 } 148 149 // procCpuTotal is the total cpu time of the process 150 type procCpuTotal struct{} 151 152 func (c procCpuTotal) Desc() *prom.Desc { 153 return prom.NewDesc( 154 "process_cpu_seconds_total", 155 "Process CPU time spent in seconds", 156 nil, sysTenantID, 157 ) 158 } 159 160 func (c procCpuTotal) Metric(ctx context.Context, s *statCaches) (prom.Metric, error) { 161 val := s.getOrInsert(cacheKeyProcess, getProcess) 162 if val == nil { 163 return nil, moerr.NewInternalError(ctx, "empty process") 164 } 165 proc := val.(*process.Process) 166 167 if cput, err := proc.TimesWithContext(ctx); err != nil { 168 return nil, err 169 } else { 170 return prom.MustNewConstMetric(c.Desc(), prom.CounterValue, CPUTotalTime(*cput)), nil 171 } 172 }