Skip to content

Commit 0cbdfb2

Browse files
committed
Add tracing
1 parent e3c695f commit 0cbdfb2

5 files changed

Lines changed: 152 additions & 11 deletions

File tree

app/app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ func wireP2P(ctx context.Context, life *lifecycle.Manager, conf Config,
405405
life.RegisterStart(lifecycle.AsyncAppCtx, lifecycle.StartP2PEventCollector, p2p.NewEventCollector(p2pNode))
406406
life.RegisterStart(lifecycle.AsyncAppCtx, lifecycle.StartP2PRouters, p2p.NewRelayRouter(p2pNode, peerIDs, relays))
407407
life.RegisterStart(lifecycle.AsyncAppCtx, lifecycle.StartForceQUICConns, p2p.UpgradeToQUICConnections(p2pNode, peerIDs))
408+
life.RegisterStart(lifecycle.AsyncAppCtx, lifecycle.StartP2PDiagnostic, p2p.NewPeerStateDiagnostic(p2pNode, peerIDs))
408409

409410
return p2pNode, nil
410411
}

app/lifecycle/order.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
StartP2PPing
2525
StartP2PRouters
2626
StartForceQUICConns
27+
StartP2PDiagnostic
2728
StartP2PConsensus
2829
StartSimulator
2930
StartScheduler

app/lifecycle/orderstart_string.go

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/run.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ func newRunCmd(runFunc func(context.Context, app.Config) error, unsafe bool) *co
3838

3939
libp2plog.SetPrimaryCore(log.LoggerCore()) // Set libp2p logger to use charon logger
4040

41+
_ = libp2plog.SetLogLevel("p2p-holepunch", "debug")
42+
_ = libp2plog.SetLogLevel("observedaddrs", "debug")
43+
4144
printLicense(cmd.Context())
4245
printFlags(cmd.Context(), cmd.Flags())
4346

p2p/p2p.go

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/libp2p/go-libp2p/core/peerstore"
2222
"github.com/libp2p/go-libp2p/core/routing"
2323
"github.com/libp2p/go-libp2p/p2p/net/swarm"
24+
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
2425
quic "github.com/libp2p/go-libp2p/p2p/transport/quic" //nolint:revive // Must be imported with alias
2526
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
2627
ma "github.com/multiformats/go-multiaddr"
@@ -106,7 +107,7 @@ func NewNode(ctx context.Context, cfg Config, key *k1.PrivateKey, connGater Conn
106107
libp2p.ConnectionGater(connGater),
107108
// Enable Autonat (required for hole punching)
108109
libp2p.EnableNATService(),
109-
libp2p.EnableHolePunching(),
110+
libp2p.EnableHolePunching(holepunch.WithTracer(newHolePunchTracer(ctx))),
110111
libp2p.AddrsFactory(func(internalAddrs []ma.Multiaddr) []ma.Multiaddr {
111112
return filterAdvertisedAddrs(externalAddrs, internalAddrs, filterPrivateAddrs)
112113
}),
@@ -263,6 +264,8 @@ func NewEventCollector(p2pNode host.Host) lifecycle.HookFuncCtx {
263264
new(event.EvtLocalReachabilityChanged),
264265
new(event.EvtLocalAddressesUpdated),
265266
new(event.EvtNATDeviceTypeChanged),
267+
new(event.EvtPeerIdentificationCompleted),
268+
new(event.EvtPeerIdentificationFailed),
266269
})
267270
if err != nil {
268271
log.Error(ctx, "Failed to subscribe to libp2p events", err)
@@ -302,6 +305,21 @@ func NewEventCollector(p2pNode host.Host) lifecycle.HookFuncCtx {
302305
z.Any("transport", evt.TransportProtocol),
303306
z.Any("nat_type", evt.NatDeviceType),
304307
)
308+
case event.EvtPeerIdentificationCompleted:
309+
isPublic := evt.ObservedAddr != nil && manet.IsPublicAddr(evt.ObservedAddr)
310+
supportsDCUtR := slices.Contains(evt.Protocols, holepunch.Protocol)
311+
log.Debug(ctx, "Peer identification completed",
312+
z.Str("peer", PeerName(evt.Peer)),
313+
z.Any("observed_addr", evt.ObservedAddr),
314+
z.Any("conn_local", evt.Conn.LocalMultiaddr()),
315+
z.Any("conn_remote", evt.Conn.RemoteMultiaddr()),
316+
z.Bool("observed_is_public", isPublic),
317+
z.Bool("peer_supports_dcutr", supportsDCUtR),
318+
)
319+
case event.EvtPeerIdentificationFailed:
320+
log.Warn(ctx, "Peer identification failed", evt.Reason,
321+
z.Str("peer", PeerName(evt.Peer)),
322+
)
305323
default:
306324
log.Warn(ctx, "Unknown libp2p event", nil, z.Str("type", fmt.Sprintf("%T", e)))
307325
}
@@ -310,6 +328,123 @@ func NewEventCollector(p2pNode host.Host) lifecycle.HookFuncCtx {
310328
}
311329
}
312330

331+
// holePunchTracer implements holepunch.EventTracer to log all DCUtR lifecycle events.
332+
type holePunchTracer struct {
333+
ctx context.Context
334+
}
335+
336+
func newHolePunchTracer(ctx context.Context) *holePunchTracer {
337+
return &holePunchTracer{ctx: log.WithTopic(ctx, "p2p")}
338+
}
339+
340+
func (t *holePunchTracer) Trace(evt *holepunch.Event) {
341+
name := PeerName(evt.Remote)
342+
switch e := evt.Evt.(type) {
343+
case *holepunch.StartHolePunchEvt:
344+
log.Debug(t.ctx, "Hole punch started",
345+
z.Str("peer", name),
346+
z.Any("remote_addrs", e.RemoteAddrs),
347+
z.Any("rtt", e.RTT),
348+
)
349+
case *holepunch.EndHolePunchEvt:
350+
if e.Success {
351+
log.Debug(t.ctx, "Hole punch succeeded",
352+
z.Str("peer", name),
353+
z.Any("elapsed", e.EllapsedTime),
354+
)
355+
} else {
356+
log.Warn(t.ctx, "Hole punch failed", errors.New(e.Error),
357+
z.Str("peer", name),
358+
z.Any("elapsed", e.EllapsedTime),
359+
)
360+
}
361+
case *holepunch.HolePunchAttemptEvt:
362+
log.Debug(t.ctx, "Hole punch attempt",
363+
z.Str("peer", name),
364+
z.Int("attempt", e.Attempt),
365+
)
366+
case *holepunch.DirectDialEvt:
367+
if e.Success {
368+
log.Debug(t.ctx, "Direct dial succeeded during hole punch",
369+
z.Str("peer", name),
370+
z.Any("elapsed", e.EllapsedTime),
371+
)
372+
} else {
373+
log.Debug(t.ctx, "Direct dial failed during hole punch",
374+
z.Str("peer", name),
375+
z.Any("elapsed", e.EllapsedTime),
376+
z.Str("error", e.Error),
377+
)
378+
}
379+
case *holepunch.ProtocolErrorEvt:
380+
log.Warn(t.ctx, "Hole punch protocol error", errors.New(e.Error),
381+
z.Str("peer", name),
382+
)
383+
default:
384+
log.Warn(t.ctx, "Unknown hole punch event", nil, z.Str("type", fmt.Sprintf("%T", evt.Evt)))
385+
}
386+
}
387+
388+
// NewPeerStateDiagnostic returns a lifecycle hook that periodically logs the connection
389+
// and peerstore address state for each cluster peer to help diagnose hole punching issues.
390+
func NewPeerStateDiagnostic(p2pNode host.Host, peers []peer.ID) lifecycle.HookFuncCtx {
391+
return func(ctx context.Context) {
392+
ctx = log.WithTopic(ctx, "p2p")
393+
394+
ticker := time.NewTicker(30 * time.Second)
395+
defer ticker.Stop()
396+
397+
for {
398+
select {
399+
case <-ctx.Done():
400+
return
401+
case <-ticker.C:
402+
}
403+
404+
selfID := p2pNode.ID()
405+
for _, pID := range peers {
406+
if pID == selfID {
407+
continue
408+
}
409+
410+
name := PeerName(pID)
411+
412+
var relayConns, directConns int
413+
414+
for _, conn := range p2pNode.Network().ConnsToPeer(pID) {
415+
if isRelayAddr(conn.RemoteMultiaddr()) {
416+
relayConns++
417+
} else {
418+
directConns++
419+
}
420+
}
421+
422+
var publicAddrs, privateAddrs, relayAddrs int
423+
424+
for _, addr := range p2pNode.Peerstore().Addrs(pID) {
425+
switch {
426+
case isRelayAddr(addr):
427+
relayAddrs++
428+
case manet.IsPublicAddr(addr):
429+
publicAddrs++
430+
default:
431+
privateAddrs++
432+
}
433+
}
434+
435+
log.Debug(ctx, "Peer connection state",
436+
z.Str("peer", name),
437+
z.Int("relay_conns", relayConns),
438+
z.Int("direct_conns", directConns),
439+
z.Int("peerstore_public_addrs", publicAddrs),
440+
z.Int("peerstore_private_addrs", privateAddrs),
441+
z.Int("peerstore_relay_addrs", relayAddrs),
442+
)
443+
}
444+
}
445+
}
446+
}
447+
313448
// peerRoutingFunc wraps a function to implement routing.PeerRouting.
314449
type peerRoutingFunc func(context.Context, peer.ID) (peer.AddrInfo, error)
315450

0 commit comments

Comments
 (0)