github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 package metrics 7 8 import ( 9 "os" 10 "runtime/metrics" 11 "runtime/pprof" 12 "strconv" 13 "strings" 14 "syscall" 15 "time" 16 17 "github.com/ethereum/go-ethereum/log" 18 ) 19 20 // Enabled is checked by the constructor functions for all of the 21 // standard metrics. If it is true, the metric returned is a stub. 22 // 23 // This global kill-switch helps quantify the observer effect and makes 24 // for less cluttered pprof profiles. 25 var Enabled = false 26 27 // enablerFlags is the CLI flag names to use to enable metrics collections. 28 var enablerFlags = []string{"metrics"} 29 30 // enablerEnvVars is the env var names to use to enable metrics collections. 31 var enablerEnvVars = []string{"GETH_METRICS"} 32 33 // init enables or disables the metrics system. Since we need this to run before 34 // any other code gets to create meters and timers, we'll actually do an ugly hack 35 // and peek into the command line args for the metrics flag. 36 func init() { 37 for _, enabler := range enablerEnvVars { 38 if val, found := syscall.Getenv(enabler); found && !Enabled { 39 if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later 40 log.Info("Enabling metrics collection") 41 Enabled = true 42 } 43 } 44 } 45 for _, arg := range os.Args { 46 flag := strings.TrimLeft(arg, "-") 47 48 for _, enabler := range enablerFlags { 49 if !Enabled && flag == enabler { 50 log.Info("Enabling metrics collection") 51 Enabled = true 52 } 53 } 54 } 55 } 56 57 var threadCreateProfile = pprof.Lookup("threadcreate") 58 59 type runtimeStats struct { 60 GCPauses *metrics.Float64Histogram 61 GCAllocBytes uint64 62 GCFreedBytes uint64 63 64 MemTotal uint64 65 HeapObjects uint64 66 HeapFree uint64 67 HeapReleased uint64 68 HeapUnused uint64 69 70 Goroutines uint64 71 SchedLatency *metrics.Float64Histogram 72 } 73 74 var runtimeSamples = []metrics.Sample{ 75 {Name: "/gc/pauses:seconds"}, // histogram 76 {Name: "/gc/heap/allocs:bytes"}, 77 {Name: "/gc/heap/frees:bytes"}, 78 {Name: "/memory/classes/total:bytes"}, 79 {Name: "/memory/classes/heap/objects:bytes"}, 80 {Name: "/memory/classes/heap/free:bytes"}, 81 {Name: "/memory/classes/heap/released:bytes"}, 82 {Name: "/memory/classes/heap/unused:bytes"}, 83 {Name: "/sched/goroutines:goroutines"}, 84 {Name: "/sched/latencies:seconds"}, // histogram 85 } 86 87 func ReadRuntimeStats() *runtimeStats { 88 r := new(runtimeStats) 89 readRuntimeStats(r) 90 return r 91 } 92 93 func readRuntimeStats(v *runtimeStats) { 94 metrics.Read(runtimeSamples) 95 for _, s := range runtimeSamples { 96 // Skip invalid/unknown metrics. This is needed because some metrics 97 // are unavailable in older Go versions, and attempting to read a 'bad' 98 // metric panics. 99 if s.Value.Kind() == metrics.KindBad { 100 continue 101 } 102 103 switch s.Name { 104 case "/gc/pauses:seconds": 105 v.GCPauses = s.Value.Float64Histogram() 106 case "/gc/heap/allocs:bytes": 107 v.GCAllocBytes = s.Value.Uint64() 108 case "/gc/heap/frees:bytes": 109 v.GCFreedBytes = s.Value.Uint64() 110 case "/memory/classes/total:bytes": 111 v.MemTotal = s.Value.Uint64() 112 case "/memory/classes/heap/objects:bytes": 113 v.HeapObjects = s.Value.Uint64() 114 case "/memory/classes/heap/free:bytes": 115 v.HeapFree = s.Value.Uint64() 116 case "/memory/classes/heap/released:bytes": 117 v.HeapReleased = s.Value.Uint64() 118 case "/memory/classes/heap/unused:bytes": 119 v.HeapUnused = s.Value.Uint64() 120 case "/sched/goroutines:goroutines": 121 v.Goroutines = s.Value.Uint64() 122 case "/sched/latencies:seconds": 123 v.SchedLatency = s.Value.Float64Histogram() 124 } 125 } 126 } 127 128 // CollectProcessMetrics periodically collects various metrics about the running process. 129 func CollectProcessMetrics(refresh time.Duration) { 130 // Short circuit if the metrics system is disabled 131 if !Enabled { 132 return 133 } 134 135 // Create the various data collectors 136 var ( 137 cpustats = make([]CPUStats, 2) 138 diskstats = make([]DiskStats, 2) 139 rstats = make([]runtimeStats, 2) 140 ) 141 142 // This scale factor is used for the runtime's time metrics. It's useful to convert to 143 // ns here because the runtime gives times in float seconds, but runtimeHistogram can 144 // only provide integers for the minimum and maximum values. 145 const secondsToNs = float64(time.Second) 146 147 // Define the various metrics to collect 148 var ( 149 cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry) 150 cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry) 151 cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry) 152 cpuSysLoadTotal = GetOrRegisterCounterFloat64("system/cpu/sysload/total", DefaultRegistry) 153 cpuSysWaitTotal = GetOrRegisterCounterFloat64("system/cpu/syswait/total", DefaultRegistry) 154 cpuProcLoadTotal = GetOrRegisterCounterFloat64("system/cpu/procload/total", DefaultRegistry) 155 cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry) 156 cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry) 157 cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil) 158 memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil) 159 memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry) 160 memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry) 161 memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry) 162 heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry) 163 heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry) 164 diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry) 165 diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry) 166 diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry) 167 diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry) 168 diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry) 169 diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry) 170 ) 171 172 var lastCollectTime time.Time 173 174 // Iterate loading the different stats and updating the meters. 175 now, prev := 0, 1 176 for ; ; now, prev = prev, now { 177 // Gather CPU times. 178 ReadCPUStats(&cpustats[now]) 179 collectTime := time.Now() 180 secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds() 181 lastCollectTime = collectTime 182 if secondsSinceLastCollect > 0 { 183 sysLoad := cpustats[now].GlobalTime - cpustats[prev].GlobalTime 184 sysWait := cpustats[now].GlobalWait - cpustats[prev].GlobalWait 185 procLoad := cpustats[now].LocalTime - cpustats[prev].LocalTime 186 // Convert to integer percentage. 187 cpuSysLoad.Update(int64(sysLoad / secondsSinceLastCollect * 100)) 188 cpuSysWait.Update(int64(sysWait / secondsSinceLastCollect * 100)) 189 cpuProcLoad.Update(int64(procLoad / secondsSinceLastCollect * 100)) 190 // increment counters (ms) 191 cpuSysLoadTotal.Inc(sysLoad) 192 cpuSysWaitTotal.Inc(sysWait) 193 cpuProcLoadTotal.Inc(procLoad) 194 } 195 196 // Threads 197 cpuThreads.Update(int64(threadCreateProfile.Count())) 198 199 // Go runtime metrics 200 readRuntimeStats(&rstats[now]) 201 202 cpuGoroutines.Update(int64(rstats[now].Goroutines)) 203 cpuSchedLatency.update(rstats[now].SchedLatency) 204 memPauses.update(rstats[now].GCPauses) 205 206 memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes)) 207 memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes)) 208 209 memTotal.Update(int64(rstats[now].MemTotal)) 210 heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased)) 211 heapObjects.Update(int64(rstats[now].HeapObjects)) 212 213 // Disk 214 if ReadDiskStats(&diskstats[now]) == nil { 215 diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount) 216 diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) 217 diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount) 218 diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) 219 diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes) 220 diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes) 221 } 222 223 time.Sleep(refresh) 224 } 225 }