github.com/klaytn/klaytn@v1.12.1/metrics/utils/metrics.go (about)

     1  // Copyright 2018 The klaytn Authors
     2  //
     3  // This file is derived from metrics/metrics.go (2018/06/04).
     4  // See LICENSE in the top directory for the original copyright and license.
     5  
     6  package metricutils
     7  
     8  import (
     9  	"fmt"
    10  	"net/http"
    11  	"os"
    12  	"runtime"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/klaytn/klaytn/log"
    17  	prometheusmetrics "github.com/klaytn/klaytn/metrics/prometheus"
    18  	"github.com/prometheus/client_golang/prometheus"
    19  	"github.com/prometheus/client_golang/prometheus/promhttp"
    20  	"github.com/rcrowley/go-metrics"
    21  	"github.com/urfave/cli/v2"
    22  )
    23  
    24  // Enabled is checked by the constructor functions for all of the
    25  // standard metrics.  If it is true, the metric returned is a stub.
    26  //
    27  // This global kill-switch helps quantify the observer effect and makes
    28  // for less cluttered pprof profiles.
    29  var Enabled = false
    30  
    31  var (
    32  	EnabledPrometheusExport = false
    33  	logger                  = log.NewModuleLogger(log.Metrics)
    34  )
    35  
    36  // MetricsEnabledFlag is the CLI flag name to use to enable metrics collections.
    37  const MetricsEnabledFlag = "metrics"
    38  
    39  const (
    40  	MetricNamespace            = "klaytn"
    41  	DashboardEnabledFlag       = "dashboard"
    42  	PrometheusExporterFlag     = "prometheus"
    43  	PrometheusExporterPortFlag = "prometheusport"
    44  )
    45  
    46  // Init enables or disables the metrics system. Since we need this to run before
    47  // any other code gets to create meters and timers, we'll actually do an ugly hack
    48  // and peek into the command line args for the metrics flag.
    49  func init() {
    50  	for _, arg := range os.Args {
    51  		if flag := strings.TrimLeft(arg, "-"); flag == MetricsEnabledFlag || flag == DashboardEnabledFlag {
    52  			Enabled = true
    53  		}
    54  		if flag := strings.TrimLeft(arg, "-"); flag == PrometheusExporterFlag {
    55  			EnabledPrometheusExport = true
    56  		}
    57  	}
    58  }
    59  
    60  // StartMetricCollectionAndExport starts exporting to prometheus and collects process metrics.
    61  func StartMetricCollectionAndExport(ctx *cli.Context) {
    62  	metricsCollectionInterval := 3 * time.Second
    63  	if Enabled {
    64  		logger.Info("Enabling metrics collection")
    65  		if EnabledPrometheusExport {
    66  			logger.Info("Enabling Prometheus Exporter")
    67  			pClient := prometheusmetrics.NewPrometheusProvider(metrics.DefaultRegistry, MetricNamespace,
    68  				"", prometheus.DefaultRegisterer, metricsCollectionInterval)
    69  			go pClient.UpdatePrometheusMetrics()
    70  			http.Handle("/metrics", promhttp.Handler())
    71  			port := ctx.Int(PrometheusExporterPortFlag)
    72  
    73  			go func() {
    74  				err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
    75  				if err != nil {
    76  					logger.Error("PrometheusExporter starting failed:", "port", port, "err", err)
    77  				}
    78  			}()
    79  		}
    80  	}
    81  	go CollectProcessMetrics(metricsCollectionInterval)
    82  }
    83  
    84  // CollectProcessMetrics periodically collects various metrics about the running process.
    85  func CollectProcessMetrics(refresh time.Duration) {
    86  	// Short circuit if the metrics system is disabled
    87  	if !Enabled {
    88  		return
    89  	}
    90  	// Create the various data collectors
    91  	memstats := make([]*runtime.MemStats, 2)
    92  	diskstats := make([]*DiskStats, 2)
    93  	for i := 0; i < len(memstats); i++ {
    94  		memstats[i] = new(runtime.MemStats)
    95  		diskstats[i] = new(DiskStats)
    96  	}
    97  	// Define the various metrics to collect
    98  	memAllocs := metrics.GetOrRegisterMeter("system/memory/allocs", metrics.DefaultRegistry)
    99  	memFrees := metrics.GetOrRegisterMeter("system/memory/frees", metrics.DefaultRegistry)
   100  	memInuse := metrics.GetOrRegisterMeter("system/memory/inuse", metrics.DefaultRegistry)
   101  	memPauses := metrics.GetOrRegisterMeter("system/memory/pauses", metrics.DefaultRegistry)
   102  
   103  	var diskReads, diskReadBytes, diskWrites, diskWriteBytes metrics.Meter
   104  	if err := ReadDiskStats(diskstats[0]); err == nil {
   105  		diskReads = metrics.GetOrRegisterMeter("system/disk/readcount", metrics.DefaultRegistry)
   106  		diskReadBytes = metrics.GetOrRegisterMeter("system/disk/readdata", metrics.DefaultRegistry)
   107  		diskWrites = metrics.GetOrRegisterMeter("system/disk/writecount", metrics.DefaultRegistry)
   108  		diskWriteBytes = metrics.GetOrRegisterMeter("system/disk/writedata", metrics.DefaultRegistry)
   109  	} else {
   110  		logger.Debug("Failed to read disk metrics", "err", err)
   111  	}
   112  	// Iterate loading the different stats and updating the meters
   113  	for i := 1; ; i++ {
   114  		runtime.ReadMemStats(memstats[i%2])
   115  		memAllocs.Mark(int64(memstats[i%2].Mallocs - memstats[(i-1)%2].Mallocs))
   116  		memFrees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees))
   117  		memInuse.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc))
   118  		memPauses.Mark(int64(memstats[i%2].PauseTotalNs - memstats[(i-1)%2].PauseTotalNs))
   119  
   120  		if ReadDiskStats(diskstats[i%2]) == nil {
   121  			diskReads.Mark(diskstats[i%2].ReadCount - diskstats[(i-1)%2].ReadCount)
   122  			diskReadBytes.Mark(diskstats[i%2].ReadBytes - diskstats[(i-1)%2].ReadBytes)
   123  			diskWrites.Mark(diskstats[i%2].WriteCount - diskstats[(i-1)%2].WriteCount)
   124  			diskWriteBytes.Mark(diskstats[i%2].WriteBytes - diskstats[(i-1)%2].WriteBytes)
   125  		}
   126  		time.Sleep(refresh)
   127  	}
   128  }