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 }