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 }