github.com/ethereum/go-ethereum@v1.16.1/metrics/metrics.go (about) 1 // Go port of Coda Hale's Metrics library 2 // 3 // <https://github.com/rcrowley/go-metrics> 4 // 5 // Coda Hale's original work: <https://github.com/codahale/metrics> 6 7 package metrics 8 9 import ( 10 "runtime/metrics" 11 "runtime/pprof" 12 "time" 13 ) 14 15 var ( 16 metricsEnabled = false 17 ) 18 19 // Enabled is checked by functions that are deemed 'expensive', e.g. if a 20 // meter-type does locking and/or non-trivial math operations during update. 21 func Enabled() bool { 22 return metricsEnabled 23 } 24 25 // Enable enables the metrics system. 26 // The Enabled-flag is expected to be set, once, during startup, but toggling off and on 27 // is not supported. 28 // 29 // Enable is not safe to call concurrently. You need to call this as early as possible in 30 // the program, before any metrics collection will happen. 31 func Enable() { 32 metricsEnabled = true 33 startMeterTickerLoop() 34 } 35 36 var threadCreateProfile = pprof.Lookup("threadcreate") 37 38 type runtimeStats struct { 39 GCPauses *metrics.Float64Histogram 40 GCAllocBytes uint64 41 GCFreedBytes uint64 42 43 MemTotal uint64 44 HeapObjects uint64 45 HeapFree uint64 46 HeapReleased uint64 47 HeapUnused uint64 48 49 Goroutines uint64 50 SchedLatency *metrics.Float64Histogram 51 } 52 53 var runtimeSamples = []metrics.Sample{ 54 {Name: "/gc/pauses:seconds"}, // histogram 55 {Name: "/gc/heap/allocs:bytes"}, 56 {Name: "/gc/heap/frees:bytes"}, 57 {Name: "/memory/classes/total:bytes"}, 58 {Name: "/memory/classes/heap/objects:bytes"}, 59 {Name: "/memory/classes/heap/free:bytes"}, 60 {Name: "/memory/classes/heap/released:bytes"}, 61 {Name: "/memory/classes/heap/unused:bytes"}, 62 {Name: "/sched/goroutines:goroutines"}, 63 {Name: "/sched/latencies:seconds"}, // histogram 64 } 65 66 func ReadRuntimeStats() *runtimeStats { 67 r := new(runtimeStats) 68 readRuntimeStats(r) 69 return r 70 } 71 72 func readRuntimeStats(v *runtimeStats) { 73 metrics.Read(runtimeSamples) 74 for _, s := range runtimeSamples { 75 // Skip invalid/unknown metrics. This is needed because some metrics 76 // are unavailable in older Go versions, and attempting to read a 'bad' 77 // metric panics. 78 if s.Value.Kind() == metrics.KindBad { 79 continue 80 } 81 82 switch s.Name { 83 case "/gc/pauses:seconds": 84 v.GCPauses = s.Value.Float64Histogram() 85 case "/gc/heap/allocs:bytes": 86 v.GCAllocBytes = s.Value.Uint64() 87 case "/gc/heap/frees:bytes": 88 v.GCFreedBytes = s.Value.Uint64() 89 case "/memory/classes/total:bytes": 90 v.MemTotal = s.Value.Uint64() 91 case "/memory/classes/heap/objects:bytes": 92 v.HeapObjects = s.Value.Uint64() 93 case "/memory/classes/heap/free:bytes": 94 v.HeapFree = s.Value.Uint64() 95 case "/memory/classes/heap/released:bytes": 96 v.HeapReleased = s.Value.Uint64() 97 case "/memory/classes/heap/unused:bytes": 98 v.HeapUnused = s.Value.Uint64() 99 case "/sched/goroutines:goroutines": 100 v.Goroutines = s.Value.Uint64() 101 case "/sched/latencies:seconds": 102 v.SchedLatency = s.Value.Float64Histogram() 103 } 104 } 105 } 106 107 // CollectProcessMetrics periodically collects various metrics about the running process. 108 func CollectProcessMetrics(refresh time.Duration) { 109 // Short circuit if the metrics system is disabled 110 if !metricsEnabled { 111 return 112 } 113 114 // Create the various data collectors 115 var ( 116 cpustats = make([]CPUStats, 2) 117 diskstats = make([]DiskStats, 2) 118 rstats = make([]runtimeStats, 2) 119 ) 120 121 // This scale factor is used for the runtime's time metrics. It's useful to convert to 122 // ns here because the runtime gives times in float seconds, but runtimeHistogram can 123 // only provide integers for the minimum and maximum values. 124 const secondsToNs = float64(time.Second) 125 126 // Define the various metrics to collect 127 var ( 128 cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) 129 cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) 130 cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) 131 cpuSysLoadTotal = GetOrRegisterCounterFloat64("system/cpu/sysload/total", DefaultRegistry) 132 cpuSysWaitTotal = GetOrRegisterCounterFloat64("system/cpu/syswait/total", DefaultRegistry) 133 cpuProcLoadTotal = GetOrRegisterCounterFloat64("system/cpu/procload/total", DefaultRegistry) 134 cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) 135 cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) 136 cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil) 137 memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil) 138 memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) 139 memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) 140 memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry) 141 heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) 142 heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry) 143 diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) 144 diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) 145 diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) 146 diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) 147 diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) 148 diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) 149 ) 150 151 var lastCollectTime time.Time 152 153 // Iterate loading the different stats and updating the meters. 154 now, prev := 0, 1 155 for ; ; now, prev = prev, now { 156 // Gather CPU times. 157 ReadCPUStats(&cpustats[now]) 158 collectTime := time.Now() 159 secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds() 160 lastCollectTime = collectTime 161 if secondsSinceLastCollect > 0 { 162 sysLoad := cpustats[now].GlobalTime - cpustats[prev].GlobalTime 163 sysWait := cpustats[now].GlobalWait - cpustats[prev].GlobalWait 164 procLoad := cpustats[now].LocalTime - cpustats[prev].LocalTime 165 // Convert to integer percentage. 166 cpuSysLoad.Update(int64(sysLoad / secondsSinceLastCollect * 100)) 167 cpuSysWait.Update(int64(sysWait / secondsSinceLastCollect * 100)) 168 cpuProcLoad.Update(int64(procLoad / secondsSinceLastCollect * 100)) 169 // increment counters (ms) 170 cpuSysLoadTotal.Inc(sysLoad) 171 cpuSysWaitTotal.Inc(sysWait) 172 cpuProcLoadTotal.Inc(procLoad) 173 } 174 175 // Threads 176 cpuThreads.Update(int64(threadCreateProfile.Count())) 177 178 // Go runtime metrics 179 readRuntimeStats(&rstats[now]) 180 181 cpuGoroutines.Update(int64(rstats[now].Goroutines)) 182 cpuSchedLatency.update(rstats[now].SchedLatency) 183 memPauses.update(rstats[now].GCPauses) 184 185 memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes)) 186 memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes)) 187 188 memTotal.Update(int64(rstats[now].MemTotal)) 189 heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased)) 190 heapObjects.Update(int64(rstats[now].HeapObjects)) 191 192 // Disk 193 if ReadDiskStats(&diskstats[now]) == nil { 194 diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount) 195 diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) 196 diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount) 197 diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) 198 diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) 199 diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) 200 } 201 202 time.Sleep(refresh) 203 } 204 }