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  }