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  }