github.com/pinpoint-apm/pinpoint-go-agent@v1.4.1-0.20240110120318-a50c2eb18c8c/stats.go (about) 1 package pinpoint 2 3 import ( 4 "os" 5 "runtime" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "github.com/shirou/gopsutil/v3/cpu" 11 "github.com/shirou/gopsutil/v3/process" 12 ) 13 14 type inspectorStats struct { 15 sampleTime time.Time 16 interval int64 17 cpuProcLoad float64 18 cpuSysLoad float64 19 heapUsed int64 20 heapMax int64 21 nonHeapUsed int64 22 nonHeapMax int64 23 gcNum int64 24 gcTime int64 25 numOpenFD int64 26 numThreads int64 27 responseAvg int64 28 responseMax int64 29 sampleNew int64 30 sampleCont int64 31 unSampleNew int64 32 unSampleCont int64 33 skipNew int64 34 skipCont int64 35 activeSpan []int32 36 } 37 38 var ( 39 proc *process.Process 40 lastMemStat runtime.MemStats 41 lastCollectTime time.Time 42 43 accResponseTime int64 44 maxResponseTime int64 45 requestCount int64 46 47 sampleNew int64 48 unSampleNew int64 49 sampleCont int64 50 unSampleCont int64 51 skipNew int64 52 skipCont int64 53 54 activeSpan sync.Map 55 ) 56 57 func initStats() { 58 var err error 59 proc, err = process.NewProcess(int32(os.Getpid())) 60 if err != nil { 61 proc = nil 62 } else { 63 proc.Percent(0) 64 } 65 66 cpu.Percent(0, false) 67 runtime.ReadMemStats(&lastMemStat) 68 lastCollectTime = time.Now() 69 activeSpan = sync.Map{} 70 } 71 72 func getNumFD() int32 { 73 if proc != nil { 74 n, _ := proc.NumFDs() 75 return n 76 } 77 return 0 78 } 79 80 func getNumThreads() int32 { 81 if proc != nil { 82 n, _ := proc.NumThreads() 83 return n 84 } 85 return 0 86 } 87 88 func getCpuLoad() (float64, float64) { 89 var procCpu float64 90 if proc != nil { 91 procCpu, _ = proc.Percent(0) 92 } else { 93 procCpu = 0 94 } 95 sysCpu, _ := cpu.Percent(0, false) 96 97 return procCpu / 100, sysCpu[0] / 100 98 } 99 100 func getStats() *inspectorStats { 101 now := time.Now() 102 procCpu, sysCpu := getCpuLoad() 103 104 var memStat runtime.MemStats 105 runtime.ReadMemStats(&memStat) 106 elapsed := now.Sub(lastCollectTime).Seconds() 107 108 stats := inspectorStats{ 109 sampleTime: now, 110 interval: int64(elapsed) * 1000, 111 cpuProcLoad: procCpu, 112 cpuSysLoad: sysCpu, 113 heapUsed: int64(memStat.HeapInuse), 114 heapMax: int64(memStat.HeapSys), 115 nonHeapUsed: int64(memStat.StackInuse), 116 nonHeapMax: int64(memStat.StackSys), 117 gcNum: int64(memStat.NumGC - lastMemStat.NumGC), 118 gcTime: int64(memStat.PauseTotalNs-lastMemStat.PauseTotalNs) / int64(time.Millisecond), 119 numOpenFD: int64(getNumFD()), 120 numThreads: int64(getNumThreads()), 121 responseAvg: calcResponseAvg(), 122 responseMax: maxResponseTime, 123 sampleNew: sampleNew, 124 sampleCont: sampleCont, 125 unSampleNew: unSampleNew, 126 unSampleCont: unSampleCont, 127 skipNew: skipNew, 128 skipCont: skipCont, 129 activeSpan: activeSpanCount(now), 130 } 131 132 lastMemStat = memStat 133 lastCollectTime = now 134 resetResponseTime() 135 136 return &stats 137 } 138 139 func calcResponseAvg() int64 { 140 if requestCount > 0 { 141 return accResponseTime / requestCount 142 } 143 144 return 0 145 } 146 147 func activeSpanCount(now time.Time) []int32 { 148 count := []int32{0, 0, 0, 0} 149 activeSpan.Range(func(k, v interface{}) bool { 150 s := v.(time.Time) 151 d := now.Sub(s).Seconds() 152 153 if d < 1 { 154 count[0]++ 155 } else if d < 3 { 156 count[1]++ 157 } else if d < 5 { 158 count[2]++ 159 } else { 160 count[3]++ 161 } 162 return true 163 }) 164 165 return count 166 } 167 168 func (agent *agent) collectAgentStatWorker() { 169 Log("stats").Infof("start collect agent stat goroutine") 170 defer agent.wg.Done() 171 172 initStats() 173 resetResponseTime() 174 175 interval := time.Duration(agent.config.Int(CfgStatCollectInterval)) * time.Millisecond 176 time.Sleep(interval) 177 cfgBatchCount := agent.config.Int(CfgStatBatchCount) 178 collected := make([]*inspectorStats, cfgBatchCount) 179 batch := 0 180 181 for agent.enable { 182 collected[batch] = getStats() 183 batch++ 184 185 if batch == cfgBatchCount { 186 agent.enqueueStat(makePAgentStatBatch(collected)) 187 batch = 0 188 } 189 time.Sleep(interval) 190 } 191 Log("stats").Infof("end collect agent stat goroutine") 192 } 193 194 func collectResponseTime(resTime int64) { 195 atomic.AddInt64(&accResponseTime, resTime) 196 atomic.AddInt64(&requestCount, 1) 197 198 if atomic.LoadInt64(&maxResponseTime) < resTime { 199 atomic.StoreInt64(&maxResponseTime, resTime) 200 } 201 } 202 203 func resetResponseTime() { 204 atomic.StoreInt64(&accResponseTime, 0) 205 atomic.StoreInt64(&requestCount, 0) 206 atomic.StoreInt64(&maxResponseTime, 0) 207 atomic.StoreInt64(&sampleNew, 0) 208 atomic.StoreInt64(&unSampleNew, 0) 209 atomic.StoreInt64(&sampleCont, 0) 210 atomic.StoreInt64(&unSampleCont, 0) 211 atomic.StoreInt64(&skipNew, 0) 212 atomic.StoreInt64(&skipCont, 0) 213 } 214 215 func addSampledActiveSpan(span *span) { 216 activeSpan.Store(span.spanId, span.startTime) 217 addRealTimeSampledActiveSpan(span) 218 } 219 220 func dropSampledActiveSpan(span *span) { 221 activeSpan.Delete(span.spanId) 222 dropRealTimeSampledActiveSpan(span) 223 } 224 225 func addUnSampledActiveSpan(span *noopSpan) { 226 activeSpan.Store(span.spanId, span.startTime) 227 addRealTimeUnSampledActiveSpan(span) 228 } 229 230 func dropUnSampledActiveSpan(span *noopSpan) { 231 activeSpan.Delete(span.spanId) 232 dropRealTimeUnSampledActiveSpan(span) 233 } 234 235 func incrSampleNew() { 236 atomic.AddInt64(&sampleNew, 1) 237 } 238 func incrUnSampleNew() { 239 atomic.AddInt64(&unSampleNew, 1) 240 } 241 func incrSampleCont() { 242 atomic.AddInt64(&sampleCont, 1) 243 } 244 func incrUnSampleCont() { 245 atomic.AddInt64(&unSampleCont, 1) 246 } 247 func incrSkipNew() { 248 atomic.AddInt64(&skipNew, 1) 249 } 250 func incrSkipCont() { 251 atomic.AddInt64(&skipCont, 1) 252 }