github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/tracer/gossipSubMeshTracer.go (about) 1 package tracer 2 3 import ( 4 "fmt" 5 "strconv" 6 "sync" 7 "time" 8 9 pubsub "github.com/libp2p/go-libp2p-pubsub" 10 "github.com/libp2p/go-libp2p/core/peer" 11 "github.com/libp2p/go-libp2p/core/protocol" 12 "github.com/rs/zerolog" 13 14 "github.com/onflow/flow-go/module" 15 "github.com/onflow/flow-go/module/component" 16 "github.com/onflow/flow-go/module/irrecoverable" 17 "github.com/onflow/flow-go/module/metrics" 18 "github.com/onflow/flow-go/network" 19 "github.com/onflow/flow-go/network/channels" 20 "github.com/onflow/flow-go/network/p2p" 21 p2pconfig "github.com/onflow/flow-go/network/p2p/config" 22 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 23 "github.com/onflow/flow-go/network/p2p/tracer/internal" 24 "github.com/onflow/flow-go/utils/logging" 25 ) 26 27 const ( 28 // MeshLogIntervalMsg is the message logged by the tracer every logInterval. 29 MeshLogIntervalMsg = "topic mesh peers of local node since last heartbeat" 30 31 // MeshLogIntervalWarnMsg is the message logged by the tracer every logInterval if there are unknown peers in the mesh. 32 MeshLogIntervalWarnMsg = "unknown peers in topic mesh peers of local node since last heartbeat" 33 34 // defaultLastHighestIHaveRPCSizeResetInterval is the interval that we reset the tracker of max ihave size sent back 35 // to a default. We use ihave message max size to determine the health of requested iwants from remote peers. However, 36 // we don't desire an ihave size anomaly to persist forever, hence, we reset it back to a default every minute. 37 // The choice of the interval to be a minute is in harmony with the GossipSub decay interval. 38 defaultLastHighestIHaveRPCSizeResetInterval = time.Minute 39 ) 40 41 // The GossipSubMeshTracer component in the GossipSub pubsub.RawTracer that is designed to track the local 42 // mesh peers for each topic. By logging the mesh peers and updating the local mesh size metric, the GossipSubMeshTracer 43 // provides insights into the behavior of the topology. 44 // 45 // This component also provides real-time and historical visibility into the topology. 46 // The GossipSubMeshTracer logs the mesh peers of the local node for each topic 47 // at a regular interval, enabling users to monitor the state of the mesh network and take appropriate action. 48 // Additionally, it allows users to configure the logging interval. 49 type GossipSubMeshTracer struct { 50 component.Component 51 topicMeshMu sync.RWMutex // to protect topicMeshMap 52 topicMeshMap map[string]map[peer.ID]struct{} // map of local mesh peers by topic. 53 logger zerolog.Logger 54 idProvider module.IdentityProvider 55 loggerInterval time.Duration 56 metrics module.LocalGossipSubRouterMetrics 57 rpcSentTracker *internal.RPCSentTracker 58 duplicateMessageTrackerCache *internal.DuplicateMessageTrackerCache 59 } 60 61 var _ p2p.PubSubTracer = (*GossipSubMeshTracer)(nil) 62 63 type RpcSentTrackerConfig struct { 64 CacheSize uint32 `validate:"gt=0"` 65 WorkerQueueCacheSize uint32 `validate:"gt=0"` 66 WorkerQueueNumber int `validate:"gt=0"` 67 } 68 69 type DuplicateMessageTrackerCacheConfig struct { 70 CacheSize uint32 `validate:"gt=0"` 71 Decay float64 `validate:"gt=0"` 72 } 73 74 type GossipSubMeshTracerConfig struct { 75 network.NetworkingType `validate:"required"` 76 metrics.HeroCacheMetricsFactory `validate:"required"` 77 Logger zerolog.Logger `validate:"required"` 78 Metrics module.LocalGossipSubRouterMetrics `validate:"required"` 79 IDProvider module.IdentityProvider `validate:"required"` 80 LoggerInterval time.Duration `validate:"required"` 81 DuplicateMessageTrackerCacheConfig p2pconfig.DuplicateMessageTrackerConfig `validate:"required"` 82 RpcSentTracker RpcSentTrackerConfig `validate:"required"` 83 } 84 85 // NewGossipSubMeshTracer creates a new *GossipSubMeshTracer. 86 // Args: 87 // - *GossipSubMeshTracerConfig: the mesh tracer config. 88 // Returns: 89 // - *GossipSubMeshTracer: new mesh tracer. 90 func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTracer { 91 lg := config.Logger.With().Str("component", "gossipsub_topology_tracer").Logger() 92 rpcSentTracker := internal.NewRPCSentTracker(&internal.RPCSentTrackerConfig{ 93 Logger: lg, 94 RPCSentCacheSize: config.RpcSentTracker.CacheSize, 95 RPCSentCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(config.HeroCacheMetricsFactory, config.NetworkingType), 96 WorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(config.HeroCacheMetricsFactory, config.NetworkingType), 97 WorkerQueueCacheSize: config.RpcSentTracker.WorkerQueueCacheSize, 98 NumOfWorkers: config.RpcSentTracker.WorkerQueueNumber, 99 LastHighestIhavesSentResetInterval: defaultLastHighestIHaveRPCSizeResetInterval, 100 }) 101 g := &GossipSubMeshTracer{ 102 topicMeshMap: make(map[string]map[peer.ID]struct{}), 103 idProvider: config.IDProvider, 104 metrics: config.Metrics, 105 logger: lg, 106 loggerInterval: config.LoggerInterval, 107 rpcSentTracker: rpcSentTracker, 108 duplicateMessageTrackerCache: internal.NewDuplicateMessageTrackerCache( 109 config.DuplicateMessageTrackerCacheConfig.CacheSize, 110 config.DuplicateMessageTrackerCacheConfig.Decay, 111 config.DuplicateMessageTrackerCacheConfig.SkipDecayThreshold, 112 config.Logger, 113 metrics.GossipSubDuplicateMessageTrackerCacheMetricFactory(config.HeroCacheMetricsFactory, config.NetworkingType), 114 ), 115 } 116 117 g.Component = component.NewComponentManagerBuilder(). 118 AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 119 ready() 120 g.logLoop(ctx) 121 }). 122 AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 123 ready() 124 lg.Debug().Msg("starting rpc sent tracker") 125 g.rpcSentTracker.Start(ctx) 126 lg.Debug().Msg("rpc sent tracker started") 127 128 <-g.rpcSentTracker.Done() 129 lg.Debug().Msg("rpc sent tracker stopped") 130 }). 131 Build() 132 133 return g 134 } 135 136 // GetLocalMeshPeers returns the local mesh peers for the given topic. 137 // Args: 138 // - topic: the topic. 139 // Returns: 140 // - []peer.ID: the local mesh peers for the given topic. 141 func (t *GossipSubMeshTracer) GetLocalMeshPeers(topic channels.Topic) []peer.ID { 142 t.topicMeshMu.RLock() 143 defer t.topicMeshMu.RUnlock() 144 145 peers := make([]peer.ID, 0, len(t.topicMeshMap[topic.String()])) 146 for p := range t.topicMeshMap[topic.String()] { 147 peers = append(peers, p) 148 } 149 return peers 150 } 151 152 // Graft is called by GossipSub when a peer is added to a topic mesh. The tracer uses this to track the mesh peers. 153 func (t *GossipSubMeshTracer) Graft(p peer.ID, topic string) { 154 t.metrics.OnPeerGraftTopic(topic) 155 t.topicMeshMu.Lock() 156 defer t.topicMeshMu.Unlock() 157 158 lg := t.logger.With().Str("topic", topic).Str("peer_id", p2plogging.PeerId(p)).Logger() 159 160 if _, ok := t.topicMeshMap[topic]; !ok { 161 t.topicMeshMap[topic] = make(map[peer.ID]struct{}) 162 } 163 t.topicMeshMap[topic][p] = struct{}{} 164 meshSize := len(t.topicMeshMap[topic]) 165 166 t.metrics.OnLocalMeshSizeUpdated(topic, meshSize) 167 lg = lg.With().Int("mesh_size", meshSize).Logger() 168 169 id, exists := t.idProvider.ByPeerID(p) 170 if !exists { 171 lg.Warn(). 172 Bool(logging.KeySuspicious, true). 173 Msg("grafted peer not found in identity provider") 174 return 175 } 176 177 lg.Debug().Hex("flow_id", logging.ID(id.NodeID)).Str("role", id.Role.String()).Msg("grafted peer") 178 } 179 180 // Prune is called by GossipSub when a peer is removed from a topic mesh. The tracer uses this to track the mesh peers. 181 func (t *GossipSubMeshTracer) Prune(p peer.ID, topic string) { 182 t.metrics.OnPeerPruneTopic(topic) 183 t.topicMeshMu.Lock() 184 defer t.topicMeshMu.Unlock() 185 186 lg := t.logger.With().Str("topic", topic).Str("peer_id", p2plogging.PeerId(p)).Logger() 187 188 if _, ok := t.topicMeshMap[topic]; !ok { 189 return 190 } 191 delete(t.topicMeshMap[topic], p) 192 193 meshSize := len(t.topicMeshMap[topic]) 194 t.metrics.OnLocalMeshSizeUpdated(topic, meshSize) 195 lg = lg.With().Int("mesh_size", meshSize).Logger() 196 197 id, exists := t.idProvider.ByPeerID(p) 198 if !exists { 199 lg.Warn(). 200 Bool(logging.KeySuspicious, true). 201 Msg("pruned peer not found in identity provider") 202 203 return 204 } 205 206 lg.Debug().Hex("flow_id", logging.ID(id.NodeID)).Str("role", id.Role.String()).Msg("pruned peer") 207 } 208 209 // SendRPC is called by GossipSub when a RPC is sent. Currently, the GossipSubMeshTracer tracks iHave RPC messages that have been sent. 210 // This function can be updated to track other control messages in the future as required. 211 func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, p peer.ID) { 212 err := t.rpcSentTracker.Track(rpc) 213 if err != nil { 214 t.logger.Err(err).Bool(logging.KeyNetworkingSecurity, true).Msg("failed to track sent pubsbub rpc") 215 } 216 217 msgCount, ihaveCount, iwantCount, graftCount, pruneCount := 0, 0, 0, 0, 0 218 if rpc.Control != nil { 219 ihaveCount = len(rpc.Control.Ihave) 220 iwantCount = len(rpc.Control.Iwant) 221 graftCount = len(rpc.Control.Graft) 222 pruneCount = len(rpc.Control.Prune) 223 } 224 msgCount = len(rpc.Publish) 225 t.metrics.OnRpcReceived(msgCount, ihaveCount, iwantCount, graftCount, pruneCount) 226 if t.logger.GetLevel() == zerolog.TraceLevel { 227 t.logger.Trace(). 228 Str("remote_peer_id", p2plogging.PeerId(p)). 229 Int("subscription_option_count", len(rpc.Subscriptions)). 230 Int("publish_message_count", msgCount). 231 Int("ihave_size", ihaveCount). 232 Int("iwant_size", iwantCount). 233 Int("graft_size", graftCount). 234 Int("prune_size", pruneCount). 235 Msg("sent pubsub rpc") 236 } 237 238 t.metrics.OnRpcSent(msgCount, ihaveCount, iwantCount, graftCount, pruneCount) 239 } 240 241 // AddPeer is called by GossipSub as a callback when a peer is added to the local node on a protocol, i.e., the local node is connected to the peer on a protocol. 242 // The peer may or may not be subscribed to any topic. 243 func (t *GossipSubMeshTracer) AddPeer(p peer.ID, proto protocol.ID) { 244 if t.logger.GetLevel() == zerolog.TraceLevel { 245 t.logger.Trace(). 246 Str("local_peer_id", p2plogging.PeerId(p)). 247 Str("protocol", string(proto)). 248 Msg("peer added") 249 } 250 t.metrics.OnPeerAddedToProtocol(string(proto)) 251 } 252 253 // RemovePeer is called by GossipSub as a callback when a peer is removed from the local node, 254 // i.e., the local node is no longer connected to the peer. 255 func (t *GossipSubMeshTracer) RemovePeer(p peer.ID) { 256 t.metrics.OnPeerRemovedFromProtocol() 257 if t.logger.GetLevel() == zerolog.TraceLevel { 258 t.logger.Trace(). 259 Str("local_peer_id", p2plogging.PeerId(p)). 260 Msg("peer removed") 261 } 262 } 263 264 // Join is called by GossipSub as a callback when the local node joins a topic. 265 func (t *GossipSubMeshTracer) Join(topic string) { 266 t.metrics.OnLocalPeerJoinedTopic() 267 if t.logger.GetLevel() == zerolog.TraceLevel { 268 t.logger.Trace(). 269 Str("topic", topic). 270 Msg("local peer joined topic") 271 } 272 } 273 274 // Leave is called by GossipSub as a callback when the local node leaves a topic. 275 func (t *GossipSubMeshTracer) Leave(topic string) { 276 t.metrics.OnLocalPeerLeftTopic() 277 if t.logger.GetLevel() == zerolog.TraceLevel { 278 t.logger.Trace(). 279 Str("topic", topic). 280 Msg("local peer left topic") 281 } 282 } 283 284 // ValidateMessage is called by GossipSub as a callback when a message is received by the local node and entered the validation phase. 285 // As the result of the validation, the message may be rejected or passed to the application (i.e., Flow protocol). 286 func (t *GossipSubMeshTracer) ValidateMessage(msg *pubsub.Message) { 287 size := len(msg.Data) 288 t.metrics.OnMessageEnteredValidation(size) 289 290 if t.logger.GetLevel() > zerolog.TraceLevel { 291 return // return fast if we are not logging at trace level 292 } 293 294 lg := t.logger.With().Logger() 295 if msg.Topic != nil { 296 lg = lg.With().Str("topic", *msg.Topic).Logger() 297 } 298 from, err := peer.IDFromBytes(msg.From) 299 if err == nil { 300 lg = lg.With().Str("remote_peer_id", p2plogging.PeerId(from)).Logger() 301 } 302 303 lg.Trace(). 304 Str("received_from", p2plogging.PeerId(msg.ReceivedFrom)). 305 Int("message_size", size). 306 Msg("received pubsub message entered validation phase") 307 } 308 309 // DeliverMessage is called by GossipSub as a callback when the local node delivers a message to all subscribers of the topic. 310 func (t *GossipSubMeshTracer) DeliverMessage(msg *pubsub.Message) { 311 size := len(msg.Data) 312 t.metrics.OnMessageDeliveredToAllSubscribers(size) 313 314 if t.logger.GetLevel() > zerolog.TraceLevel { 315 return // return fast if we are not logging at trace level 316 } 317 318 lg := t.logger.With().Logger() 319 if msg.Topic != nil { 320 lg = lg.With().Str("topic", *msg.Topic).Logger() 321 } 322 from, err := peer.IDFromBytes(msg.From) 323 if err == nil { 324 lg = lg.With().Str("remote_peer_id", p2plogging.PeerId(from)).Logger() 325 } 326 327 lg.Trace(). 328 Str("received_from", p2plogging.PeerId(msg.ReceivedFrom)). 329 Int("message_size", len(msg.Data)). 330 Msg("delivered pubsub message to all subscribers") 331 } 332 333 // RejectMessage is called by GossipSub as a callback when a message is rejected by the local node. 334 // The message may be rejected for a variety of reasons, but the most common reason is that the message is invalid with respect to signature. 335 // Any message that arrives at the local node should contain the peer id of the source (i.e., the peer that created the message), the 336 // networking public key of the source, and the signature of the message. The local node uses this information to verify the message. 337 // If any of the information is missing or invalid, the message is rejected. 338 func (t *GossipSubMeshTracer) RejectMessage(msg *pubsub.Message, reason string) { 339 size := len(msg.Data) 340 t.metrics.OnMessageRejected(size, reason) 341 342 if t.logger.GetLevel() > zerolog.TraceLevel { 343 return // return fast if we are not logging at trace level 344 } 345 346 lg := t.logger.With().Logger() 347 if msg.Topic != nil { 348 lg = lg.With().Str("topic", *msg.Topic).Logger() 349 } 350 from, err := peer.IDFromBytes(msg.From) 351 if err == nil { 352 lg = lg.With().Str("remote_peer_id", p2plogging.PeerId(from)).Logger() 353 } 354 355 lg.Trace(). 356 Str("received_from", p2plogging.PeerId(msg.ReceivedFrom)). 357 Int("message_size", size). 358 Msg("rejected pubsub message") 359 360 } 361 362 // DuplicateMessage is called by GossipSub as a callback when a duplicate message is received by the local node. 363 func (t *GossipSubMeshTracer) DuplicateMessage(msg *pubsub.Message) { 364 size := len(msg.Data) 365 t.metrics.OnMessageDuplicate(size) 366 367 if t.logger.GetLevel() > zerolog.TraceLevel { 368 return // return fast if we are not logging at trace level 369 } 370 371 lg := t.logger.With().Logger() 372 if msg.Topic != nil { 373 lg = lg.With().Str("topic", *msg.Topic).Logger() 374 } 375 from, err := peer.IDFromBytes(msg.From) 376 if err == nil { 377 lg = lg.With().Str("remote_peer_id", p2plogging.PeerId(from)).Logger() 378 } 379 380 count, err := t.duplicateMessageTrackerCache.DuplicateMessageReceived(msg.ReceivedFrom) 381 if err != nil { 382 t.logger.Fatal(). 383 Err(err). 384 Bool(logging.KeyNetworkingSecurity, true). 385 Msg("failed to increment gossipsub duplicate message tracker count for peer") 386 return 387 } 388 389 lg.Trace(). 390 Str("received_from", p2plogging.PeerId(msg.ReceivedFrom)). 391 Int("message_size", size). 392 Float64("duplicate_message_count", count). 393 Msg("received duplicate pubsub message") 394 395 } 396 397 // ThrottlePeer is called by GossipSub when a peer is throttled by the local node, i.e., the local node is not accepting any 398 // pubsub message from the peer but may still accept control messages. 399 func (t *GossipSubMeshTracer) ThrottlePeer(p peer.ID) { 400 t.logger.Warn(). 401 Bool(logging.KeyNetworkingSecurity, true). 402 Str("remote_peer_id", p2plogging.PeerId(p)). 403 Msg("throttled peer; no longer accepting pubsub messages from peer, but may still accept control messages") 404 t.metrics.OnPeerThrottled() 405 } 406 407 // RecvRPC is called by GossipSub as a callback when an inbound RPC message is received by the local node, 408 // note that the RPC already passed the RPC inspection, hence its statistics may be different from the RPC inspector metrics, as 409 // the RPC inspector metrics are updated before the RPC inspection, and the RPC may gone through truncation or rejection. 410 // This callback tracks the RPC messages as they are completely received by the local GossipSub router. 411 func (t *GossipSubMeshTracer) RecvRPC(rpc *pubsub.RPC) { 412 msgCount, ihaveCount, iwantCount, graftCount, pruneCount := 0, 0, 0, 0, 0 413 if rpc.Control != nil { 414 ihaveCount = len(rpc.Control.Ihave) 415 iwantCount = len(rpc.Control.Iwant) 416 graftCount = len(rpc.Control.Graft) 417 pruneCount = len(rpc.Control.Prune) 418 } 419 msgCount = len(rpc.Publish) 420 t.metrics.OnRpcReceived(msgCount, ihaveCount, iwantCount, graftCount, pruneCount) 421 if t.logger.GetLevel() == zerolog.TraceLevel { 422 t.logger.Trace(). 423 Int("subscription_option_count", len(rpc.Subscriptions)). 424 Int("publish_message_count", msgCount). 425 Int("ihave_size", ihaveCount). 426 Int("iwant_size", iwantCount). 427 Int("graft_size", graftCount). 428 Int("prune_size", pruneCount). 429 Msg("received pubsub rpc") 430 } 431 } 432 433 // DropRPC is called by GossipSub as a callback when an outbound RPC message is dropped by the local node, typically because the local node 434 // outbound message queue is full; or the RPC is big and the local node cannot fragment it. 435 func (t *GossipSubMeshTracer) DropRPC(rpc *pubsub.RPC, p peer.ID) { 436 msgCount, ihaveCount, iwantCount, graftCount, pruneCount := 0, 0, 0, 0, 0 437 if rpc.Control != nil { 438 ihaveCount = len(rpc.Control.Ihave) 439 iwantCount = len(rpc.Control.Iwant) 440 graftCount = len(rpc.Control.Graft) 441 pruneCount = len(rpc.Control.Prune) 442 } 443 msgCount = len(rpc.Publish) 444 t.metrics.OnRpcReceived(msgCount, ihaveCount, iwantCount, graftCount, pruneCount) 445 if t.logger.GetLevel() == zerolog.TraceLevel { 446 t.logger.Warn(). 447 Bool(logging.KeyNetworkingSecurity, true). 448 Str("remote_peer_id", p2plogging.PeerId(p)). 449 Int("subscription_option_count", len(rpc.Subscriptions)). 450 Int("publish_message_count", msgCount). 451 Int("ihave_size", ihaveCount). 452 Int("iwant_size", iwantCount). 453 Int("graft_size", graftCount). 454 Int("prune_size", pruneCount). 455 Msg("outbound rpc dropped") 456 } 457 t.metrics.OnOutboundRpcDropped() 458 } 459 460 // UndeliverableMessage is called by GossipSub as a callback when a message is dropped by the local node, typically because the local node 461 // outbound message queue is full; or the message is big and the local node cannot fragment it. 462 func (t *GossipSubMeshTracer) UndeliverableMessage(msg *pubsub.Message) { 463 t.logger.Warn(). 464 Bool(logging.KeyNetworkingSecurity, true). 465 Str("topic", *msg.Topic). 466 Str("remote_peer_id", p2plogging.PeerId(msg.ReceivedFrom)). 467 Int("message_size", len(msg.Data)). 468 Msg("undeliverable pubsub message") 469 t.metrics.OnUndeliveredMessage() 470 } 471 472 // WasIHaveRPCSent returns true if an iHave control message for the messageID was sent, otherwise false. 473 func (t *GossipSubMeshTracer) WasIHaveRPCSent(messageID string) bool { 474 return t.rpcSentTracker.WasIHaveRPCSent(messageID) 475 } 476 477 // LastHighestIHaveRPCSize returns the last highest RPC iHave message sent. 478 func (t *GossipSubMeshTracer) LastHighestIHaveRPCSize() int64 { 479 return t.rpcSentTracker.LastHighestIHaveRPCSize() 480 } 481 482 // DuplicateMessageCount returns the current duplicate message count for the peer. 483 func (t *GossipSubMeshTracer) DuplicateMessageCount(peerID peer.ID) float64 { 484 count, found, err := t.duplicateMessageTrackerCache.GetWithInit(peerID) 485 if err != nil { 486 t.logger.Fatal(). 487 Err(err). 488 Bool(logging.KeyNetworkingSecurity, true). 489 Str("peer_id", p2plogging.PeerId(peerID)). 490 Msg("failed to get duplicate message count for peer") 491 return 0 492 } 493 if !found { 494 t.logger.Fatal(). 495 Err(err). 496 Bool(logging.KeyNetworkingSecurity, true). 497 Str("peer_id", peerID.String()). 498 Msg("failed to initialize duplicate message count for peer during get with init") 499 return 0 500 } 501 return count 502 } 503 504 // logLoop logs the mesh peers of the local node for each topic at a regular interval. 505 func (t *GossipSubMeshTracer) logLoop(ctx irrecoverable.SignalerContext) { 506 ticker := time.NewTicker(t.loggerInterval) 507 defer ticker.Stop() 508 509 for { 510 select { 511 case <-ctx.Done(): 512 return 513 default: 514 } 515 516 select { 517 case <-ctx.Done(): 518 return 519 case <-ticker.C: 520 t.logPeers() 521 } 522 } 523 } 524 525 // logPeers logs the mesh peers of the local node for each topic. 526 // Note that based on GossipSub parameters, we expect to have between 6 and 12 peers in the mesh for each topic. 527 // Hence, choosing a heartbeat interval in the order of minutes should be sufficient to log the mesh peers of the local node. 528 // Also, note that the mesh peers are also logged reactively when a peer is added or removed from the mesh. 529 func (t *GossipSubMeshTracer) logPeers() { 530 t.topicMeshMu.RLock() 531 defer t.topicMeshMu.RUnlock() 532 for topic := range t.topicMeshMap { 533 shouldWarn := false // whether we should warn about the mesh state 534 535 topicPeers := zerolog.Dict() 536 537 peerIndex := -1 // index to keep track of peer info in different logging dictionaries. 538 for p := range t.topicMeshMap[topic] { 539 peerIndex++ 540 id, exists := t.idProvider.ByPeerID(p) 541 542 if !exists { 543 shouldWarn = true 544 topicPeers = topicPeers.Str(strconv.Itoa(peerIndex), fmt.Sprintf("pid=%s, flow_id=unknown, role=unknown", p2plogging.PeerId(p))) 545 continue 546 } 547 548 topicPeers = topicPeers.Str(strconv.Itoa(peerIndex), fmt.Sprintf("pid=%s, flow_id=%x, role=%s", p2plogging.PeerId(p), id.NodeID, id.Role.String())) 549 } 550 551 lg := t.logger.With(). 552 Dur("heartbeat_interval", t.loggerInterval). 553 Str("topic", topic). 554 Dict("topic_mesh", topicPeers). 555 Logger() 556 557 if shouldWarn { 558 lg.Warn(). 559 Bool(logging.KeySuspicious, true). 560 Msg(MeshLogIntervalWarnMsg) 561 continue 562 } 563 lg.Debug().Msg(MeshLogIntervalMsg) 564 } 565 }