github.com/iotexproject/iotex-core@v1.14.1-rc1/server/itx/heartbeat.go (about)

     1  // Copyright (c) 2022 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package itx
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"runtime"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/iotexproject/go-fsm"
    16  	"github.com/pkg/errors"
    17  	"github.com/prometheus/client_golang/prometheus"
    18  	"go.uber.org/zap"
    19  
    20  	"github.com/iotexproject/iotex-core/consensus"
    21  	"github.com/iotexproject/iotex-core/consensus/scheme"
    22  	"github.com/iotexproject/iotex-core/consensus/scheme/rolldpos"
    23  	"github.com/iotexproject/iotex-core/dispatcher"
    24  	"github.com/iotexproject/iotex-core/p2p"
    25  	"github.com/iotexproject/iotex-core/pkg/log"
    26  	"github.com/iotexproject/iotex-core/pkg/version"
    27  	statedb "github.com/iotexproject/iotex-core/state"
    28  )
    29  
    30  // TODO: HeartbeatHandler opens encapsulation of a few structs to inspect the internal status, we need to find a better
    31  // approach to do so in the future
    32  
    33  var _heartbeatMtc = prometheus.NewGaugeVec(
    34  	prometheus.GaugeOpts{
    35  		Name: "iotex_heartbeat_status",
    36  		Help: "Node heartbeat status.",
    37  	},
    38  	[]string{"status_type", "source"},
    39  )
    40  
    41  var _versionMtc = prometheus.NewGaugeVec(
    42  	prometheus.GaugeOpts{
    43  		Name: "iotex_version_status",
    44  		Help: "Node software version status.",
    45  	},
    46  	[]string{"type", "value"},
    47  )
    48  
    49  func init() {
    50  	prometheus.MustRegister(_heartbeatMtc)
    51  	prometheus.MustRegister(_versionMtc)
    52  }
    53  
    54  // HeartbeatHandler is the handler to periodically log the system key metrics
    55  type HeartbeatHandler struct {
    56  	s *Server
    57  	l *zap.Logger
    58  }
    59  
    60  // NewHeartbeatHandler instantiates a HeartbeatHandler instance
    61  func NewHeartbeatHandler(s *Server, cfg p2p.Config) *HeartbeatHandler {
    62  	return &HeartbeatHandler{
    63  		s: s,
    64  		l: log.L().With(zap.String("networkAddr", fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))),
    65  	}
    66  }
    67  
    68  // Log executes the logging logic
    69  func (h *HeartbeatHandler) Log() {
    70  	// operator address
    71  	cfg := h.s.Config().Chain
    72  	_heartbeatMtc.WithLabelValues("operatorAddress", cfg.ProducerAddress().String()).Set(1)
    73  
    74  	// Dispatcher metrics
    75  	dp, ok := h.s.Dispatcher().(*dispatcher.IotxDispatcher)
    76  	if !ok {
    77  		h.l.Error("dispatcher is not the instance of IotxDispatcher")
    78  		return
    79  	}
    80  	numDPEvts := dp.EventQueueSize()
    81  	totalDPEventNumber := 0
    82  	events := []string{}
    83  	for event, num := range numDPEvts {
    84  		totalDPEventNumber += num
    85  		events = append(events, event+":"+strconv.Itoa(num))
    86  	}
    87  	dpEvtsAudit, err := json.Marshal(dp.EventAudit())
    88  	if err != nil {
    89  		h.l.Error("error when serializing the dispatcher event audit map.", zap.Error(err))
    90  		return
    91  	}
    92  
    93  	// Network metrics
    94  	peers, err := h.s.P2PAgent().ConnectedPeers()
    95  	if err != nil {
    96  		h.l.Debug("error when get connectedPeers.", zap.Error(err))
    97  		peers = nil
    98  	}
    99  
   100  	numPeers := len(peers)
   101  	h.l.Debug("Node status.",
   102  		zap.Int("numConnectedPeers", numPeers),
   103  		zap.String("pendingDispatcherEvents", "{"+strings.Join(events, ", ")+"}"),
   104  		zap.String("pendingDispatcherEventsAudit", string(dpEvtsAudit)))
   105  
   106  	_heartbeatMtc.WithLabelValues("numConnectedPeers", "node").Set(float64(numPeers))
   107  	_heartbeatMtc.WithLabelValues("pendingDispatcherEvents", "node").Set(float64(totalDPEventNumber))
   108  	// chain service
   109  	for _, c := range h.s.chainservices {
   110  		// Consensus metrics
   111  		cs, ok := c.Consensus().(*consensus.IotxConsensus)
   112  		if !ok {
   113  			h.l.Info("consensus is not the instance of IotxConsensus.")
   114  			return
   115  		}
   116  		rolldpos, ok := cs.Scheme().(*rolldpos.RollDPoS)
   117  		numPendingEvts := 0
   118  		consensusEpoch := uint64(0)
   119  		consensusHeight := uint64(0)
   120  		height := c.Blockchain().TipHeight()
   121  
   122  		var consensusMetrics scheme.ConsensusMetrics
   123  		var state fsm.State
   124  		if ok {
   125  			numPendingEvts = rolldpos.NumPendingEvts()
   126  			state = rolldpos.CurrentState()
   127  
   128  			// RollDpos Consensus Metrics
   129  			consensusMetrics, err = rolldpos.Metrics()
   130  			if err != nil {
   131  				if height > 0 || errors.Cause(err) != statedb.ErrStateNotExist {
   132  					h.l.Error("failed to read consensus metrics", zap.Error(err))
   133  					return
   134  				}
   135  			}
   136  			consensusEpoch = consensusMetrics.LatestEpoch
   137  			consensusHeight = consensusMetrics.LatestHeight
   138  		} else {
   139  			h.l.Debug("scheme is not the instance of RollDPoS")
   140  		}
   141  
   142  		// Block metrics
   143  		actPoolSize := c.ActionPool().GetSize()
   144  		actPoolCapacity := c.ActionPool().GetCapacity()
   145  		targetHeight := c.BlockSync().TargetHeight()
   146  
   147  		h.l.Debug("chain service status",
   148  			zap.Int("rolldposEvents", numPendingEvts),
   149  			zap.String("fsmState", string(state)),
   150  			zap.Uint64("blockchainHeight", height),
   151  			zap.Uint64("actpoolSize", actPoolSize),
   152  			zap.Uint64("actpoolCapacity", actPoolCapacity),
   153  			zap.Uint32("chainID", c.ChainID()),
   154  			zap.Uint64("targetHeight", targetHeight),
   155  			zap.Uint64("concensusEpoch", consensusEpoch),
   156  			zap.Uint64("consensusHeight", consensusHeight),
   157  		)
   158  
   159  		chainIDStr := strconv.FormatUint(uint64(c.ChainID()), 10)
   160  		_heartbeatMtc.WithLabelValues("consensusEpoch", chainIDStr).Set(float64(consensusHeight))
   161  		_heartbeatMtc.WithLabelValues("consensusRound", chainIDStr).Set(float64(consensusEpoch))
   162  		_heartbeatMtc.WithLabelValues("pendingRolldposEvents", chainIDStr).Set(float64(numPendingEvts))
   163  		_heartbeatMtc.WithLabelValues("blockchainHeight", chainIDStr).Set(float64(height))
   164  		_heartbeatMtc.WithLabelValues("actpoolSize", chainIDStr).Set(float64(actPoolSize))
   165  		_heartbeatMtc.WithLabelValues("actpoolGasInPool", chainIDStr).Set(float64(c.ActionPool().GetGasSize()))
   166  		_heartbeatMtc.WithLabelValues("actpoolCapacity", chainIDStr).Set(float64(actPoolCapacity))
   167  		_heartbeatMtc.WithLabelValues("targetHeight", chainIDStr).Set(float64(targetHeight))
   168  		_heartbeatMtc.WithLabelValues("packageVersion", version.PackageVersion).Set(1)
   169  		_heartbeatMtc.WithLabelValues("packageCommitID", version.PackageCommitID).Set(1)
   170  		_heartbeatMtc.WithLabelValues("goVersion", version.GoVersion).Set(1)
   171  	}
   172  
   173  	// Mem metrics
   174  	memMetrics()
   175  }
   176  
   177  func memMetrics() {
   178  	bToMb := func(b uint64) uint64 {
   179  		return b / 1024 / 1024
   180  	}
   181  	var memStat runtime.MemStats
   182  	runtime.ReadMemStats(&memStat)
   183  	_heartbeatMtc.WithLabelValues("allocatedHeapObjects", "node").Set(float64(bToMb(memStat.Alloc)))
   184  	_heartbeatMtc.WithLabelValues("totalAllocatedHeapObjects", "node").Set(float64(bToMb(memStat.TotalAlloc)))
   185  	_heartbeatMtc.WithLabelValues("stackInUse", "node").Set(float64(bToMb(memStat.StackInuse)))
   186  	_heartbeatMtc.WithLabelValues("stackFromOS", "node").Set(float64(bToMb(memStat.StackSys)))
   187  	_heartbeatMtc.WithLabelValues("totalFromOS", "node").Set(float64(bToMb(memStat.Sys)))
   188  	_heartbeatMtc.WithLabelValues("heapInUse", "node").Set(float64(bToMb(memStat.HeapInuse)))
   189  	_heartbeatMtc.WithLabelValues("heapFromOS", "node").Set(float64(bToMb(memStat.HeapSys)))
   190  	_heartbeatMtc.WithLabelValues("heapIdle", "node").Set(float64(bToMb(memStat.HeapIdle)))
   191  	_heartbeatMtc.WithLabelValues("heapReleased", "node").Set(float64(bToMb(memStat.HeapReleased)))
   192  	_heartbeatMtc.WithLabelValues("numberOfGC", "node").Set(float64(memStat.NumGC))
   193  	_heartbeatMtc.WithLabelValues("numberOfRoutines", "node").Set(float64(runtime.NumGoroutine()))
   194  }