go.temporal.io/server@v1.23.0/common/metrics/runtime.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package metrics 26 27 import ( 28 "runtime" 29 "sync/atomic" 30 "time" 31 32 "go.temporal.io/server/common/build" 33 "go.temporal.io/server/common/headers" 34 "go.temporal.io/server/common/log" 35 ) 36 37 const ( 38 // buildInfoMetricName is the emitted build information metric's name. 39 buildInfoMetricName = "build_information" 40 41 // buildAgeMetricName is the emitted build age metric's name. 42 buildAgeMetricName = "build_age" 43 ) 44 45 // RuntimeMetricsReporter A struct containing the state of the RuntimeMetricsReporter. 46 type RuntimeMetricsReporter struct { 47 handler Handler 48 buildInfoHandler Handler 49 reportInterval time.Duration 50 started int32 51 quit chan struct{} 52 logger log.Logger 53 lastNumGC uint32 54 buildTime time.Time 55 } 56 57 // NewRuntimeMetricsReporter Creates a new RuntimeMetricsReporter. 58 func NewRuntimeMetricsReporter( 59 handler Handler, 60 reportInterval time.Duration, 61 logger log.Logger, 62 instanceID string, 63 ) *RuntimeMetricsReporter { 64 if len(instanceID) > 0 { 65 handler = handler.WithTags(StringTag(instance, instanceID)) 66 } 67 var memstats runtime.MemStats 68 runtime.ReadMemStats(&memstats) 69 70 return &RuntimeMetricsReporter{ 71 handler: handler, 72 reportInterval: reportInterval, 73 logger: logger, 74 lastNumGC: memstats.NumGC, 75 quit: make(chan struct{}), 76 buildTime: build.InfoData.GitTime, 77 buildInfoHandler: handler.WithTags( 78 StringTag(gitRevisionTag, build.InfoData.GitRevision), 79 StringTag(buildDateTag, build.InfoData.GitTime.Format(time.RFC3339)), 80 StringTag(buildPlatformTag, build.InfoData.GoArch), 81 StringTag(goVersionTag, build.InfoData.GoVersion), 82 StringTag(buildVersionTag, headers.ServerVersion), 83 ), 84 } 85 } 86 87 // report Sends runtime metrics to the local metrics collector. 88 func (r *RuntimeMetricsReporter) report() { 89 var memStats runtime.MemStats 90 runtime.ReadMemStats(&memStats) 91 92 r.handler.Gauge(NumGoRoutinesGauge).Record(float64(runtime.NumGoroutine())) 93 r.handler.Gauge(GoMaxProcsGauge).Record(float64(runtime.GOMAXPROCS(0))) 94 r.handler.Gauge(MemoryAllocatedGauge).Record(float64(memStats.Alloc)) 95 r.handler.Gauge(MemoryHeapGauge).Record(float64(memStats.HeapAlloc)) 96 r.handler.Gauge(MemoryHeapIdleGauge).Record(float64(memStats.HeapIdle)) 97 r.handler.Gauge(MemoryHeapInuseGauge).Record(float64(memStats.HeapInuse)) 98 r.handler.Gauge(MemoryStackGauge).Record(float64(memStats.StackInuse)) 99 100 // memStats.NumGC is a perpetually incrementing counter (unless it wraps at 2^32) 101 num := memStats.NumGC 102 lastNum := atomic.SwapUint32(&r.lastNumGC, num) // reset for the next iteration 103 if delta := num - lastNum; delta > 0 { 104 r.handler.Histogram(NumGCCounter, Bytes).Record(int64(delta)) 105 if delta > 255 { 106 // too many GCs happened, the timestamps buffer got wrapped around. Report only the last 256 107 lastNum = num - 256 108 } 109 for i := lastNum; i != num; i++ { 110 pause := memStats.PauseNs[i%256] 111 r.handler.Timer(GcPauseMsTimer).Record(time.Duration(pause)) 112 } 113 } 114 115 // report build info 116 r.buildInfoHandler.Gauge(buildInfoMetricName).Record(1.0) 117 r.buildInfoHandler.Gauge(buildAgeMetricName).Record(float64(time.Since(r.buildTime))) 118 } 119 120 // Start Starts the reporter thread that periodically emits metrics. 121 func (r *RuntimeMetricsReporter) Start() { 122 if !atomic.CompareAndSwapInt32(&r.started, 0, 1) { 123 return 124 } 125 r.report() 126 go func() { 127 ticker := time.NewTicker(r.reportInterval) 128 for { 129 select { 130 case <-ticker.C: 131 r.report() 132 case <-r.quit: 133 ticker.Stop() 134 return 135 } 136 } 137 }() 138 r.logger.Info("RuntimeMetricsReporter started") 139 } 140 141 // Stop Stops reporting of runtime metrics. The reporter cannot be started again after it's been stopped. 142 func (r *RuntimeMetricsReporter) Stop() { 143 close(r.quit) 144 r.logger.Info("RuntimeMetricsReporter stopped") 145 }