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