From 1f25f170d15e2a89ab4ad99554631aee0c00ca8e Mon Sep 17 00:00:00 2001 From: sbackend Date: Wed, 19 Aug 2026 00:31:01 +0200 Subject: [PATCH 1/2] feat: add new metrics --- pkg/p2p/libp2p/libp2p.go | 6 +-- pkg/p2p/libp2p/metrics.go | 98 +++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/pkg/p2p/libp2p/libp2p.go b/pkg/p2p/libp2p/libp2p.go index 3289ba4b236..11eaee9fd2a 100644 --- a/pkg/p2p/libp2p/libp2p.go +++ b/pkg/p2p/libp2p/libp2p.go @@ -1214,7 +1214,7 @@ func (s *Service) Connect(ctx context.Context, addrs []ma.Multiaddr) (address *b return nil, fmt.Errorf("libp2p connect: peer %s does not exist %w", overlay, p2p.ErrPeerNotFound) } - s.metrics.CreatedConnectionCount.Inc() + s.metrics.incCreatedConnection(stream.Conn().RemoteMultiaddr()) if len(peerAddrs) > 0 { s.notifyReacherConnected(overlay, peerAddrs) @@ -1572,8 +1572,8 @@ type connectionNotifier struct { network.Notifiee } -func (c *connectionNotifier) Connected(_ network.Network, _ network.Conn) { - c.metrics.HandledConnectionCount.Inc() +func (c *connectionNotifier) Connected(_ network.Network, conn network.Conn) { + c.metrics.observeHandledConnection(conn.RemoteMultiaddr()) } // isNetworkOrHostUnreachableError determines based on the diff --git a/pkg/p2p/libp2p/metrics.go b/pkg/p2p/libp2p/metrics.go index a98035913e4..415ef0402f0 100644 --- a/pkg/p2p/libp2p/metrics.go +++ b/pkg/p2p/libp2p/metrics.go @@ -5,16 +5,26 @@ package libp2p import ( + "github.com/ethersphere/bee/v2/pkg/bzz" m "github.com/ethersphere/bee/v2/pkg/metrics" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" "github.com/prometheus/client_golang/prometheus" ) +const ( + connectionTransportLabelName = "transport" + connectionTransportHelp = "The 'transport' label is one of: tcp, ws, wss, quic-v1, quic, unknown." +) + type metrics struct { // all metrics fields must be exported // to be able to return them by Metrics() // using reflection - CreatedConnectionCount prometheus.Counter - HandledConnectionCount prometheus.Counter + CreatedConnectionCount *prometheus.CounterVec + HandledConnectionCount *prometheus.CounterVec + PublicAddressConnections *prometheus.CounterVec + PrivateAddressConnections *prometheus.CounterVec CreatedStreamCount prometheus.Counter ClosedStreamCount prometheus.Counter StreamResetCount prometheus.Counter @@ -33,18 +43,42 @@ func newMetrics() metrics { subsystem := "libp2p" return metrics{ - CreatedConnectionCount: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "created_connection_count", - Help: "Number of initiated outgoing libp2p connections.", - }), - HandledConnectionCount: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "handled_connection_count", - Help: "Number of handled incoming libp2p connections.", - }), + CreatedConnectionCount: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "created_connection_count", + Help: "Number of initiated outgoing libp2p connections. " + connectionTransportHelp, + }, + []string{connectionTransportLabelName}, + ), + HandledConnectionCount: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "handled_connection_count", + Help: "Number of handled incoming libp2p connections. " + connectionTransportHelp, + }, + []string{connectionTransportLabelName}, + ), + PublicAddressConnections: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "public_address_connections_total", + Help: "Number of libp2p connections whose remote multiaddr is a public address. " + connectionTransportHelp, + }, + []string{connectionTransportLabelName}, + ), + PrivateAddressConnections: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "private_address_connections_total", + Help: "Number of libp2p connections whose remote multiaddr is a private address. " + connectionTransportHelp, + }, + []string{connectionTransportLabelName}, + ), CreatedStreamCount: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: m.Namespace, Subsystem: subsystem, @@ -120,6 +154,42 @@ func newMetrics() metrics { } } +func (m metrics) incCreatedConnection(addr ma.Multiaddr) { + m.CreatedConnectionCount.WithLabelValues(connectionTransportLabel(addr)).Inc() +} + +func (m metrics) observeHandledConnection(addr ma.Multiaddr) { + transport := connectionTransportLabel(addr) + m.HandledConnectionCount.WithLabelValues(transport).Inc() + if manet.IsPublicAddr(addr) { + m.PublicAddressConnections.WithLabelValues(transport).Inc() + return + } + m.PrivateAddressConnections.WithLabelValues(transport).Inc() +} + +// connectionTransportLabel returns the Prometheus transport label for a connection +// multiaddr. Live WSS connections are often encoded with the deprecated /wss +// component rather than the /tls/.../ws form used in advertised AutoTLS addresses. +func connectionTransportLabel(addr ma.Multiaddr) string { + if addr == nil { + return bzz.TransportUnknown.String() + } + if _, err := addr.ValueForProtocol(ma.P_WSS); err == nil { + return bzz.TransportWSS.String() + } + if t := bzz.ClassifyTransport(addr); t != bzz.TransportUnknown { + return t.String() + } + if _, err := addr.ValueForProtocol(ma.P_QUIC_V1); err == nil { + return "quic-v1" + } + if _, err := addr.ValueForProtocol(ma.P_QUIC); err == nil { + return "quic" + } + return bzz.TransportUnknown.String() +} + func (s *Service) Metrics() []prometheus.Collector { collectors := append(m.PrometheusCollectorsFromFields(s.metrics), s.handshakeService.Metrics()...) if mc, ok := s.reacher.(interface{ Metrics() []prometheus.Collector }); ok { From 66a4ec3c4921763635f5fac95db56551dd2f767a Mon Sep 17 00:00:00 2001 From: sbackend Date: Thu, 20 Aug 2026 12:48:40 +0200 Subject: [PATCH 2/2] fix: update metrics --- pkg/bzz/transport.go | 23 +++++++- pkg/p2p/libp2p/metrics.go | 115 +++++++++++++++++--------------------- 2 files changed, 73 insertions(+), 65 deletions(-) diff --git a/pkg/bzz/transport.go b/pkg/bzz/transport.go index 6207c6f1c20..66d9cfc8825 100644 --- a/pkg/bzz/transport.go +++ b/pkg/bzz/transport.go @@ -24,6 +24,10 @@ const ( TransportWS // TransportWSS indicates WebSocket with TLS (secure). TransportWSS + // TransportQUICV1 indicates QUIC (version 1) transport. + TransportQUICV1 + // TransportQUIC indicates legacy QUIC transport. + TransportQUIC ) // String returns a string representation of the transport type. @@ -35,13 +39,17 @@ func (t TransportType) String() string { return "ws" case TransportWSS: return "wss" + case TransportQUICV1: + return "quic-v1" + case TransportQUIC: + return "quic" default: return "unknown" } } // Priority returns the sorting priority for the transport type. -// Lower value = higher priority: TCP (0) > WS (1) > WSS (2) > Unknown (3) +// Lower value = higher priority: TCP(0) > WS(1) > WSS(2) > QUIC/Unknown(3). func (t TransportType) Priority() int { switch t { case TransportTCP: @@ -50,6 +58,10 @@ func (t TransportType) Priority() int { return 1 case TransportWSS: return 2 + case TransportQUICV1, TransportQUIC: + // Treat QUIC like unknown for address sorting so existing selection + // behavior stays stable. + return 3 default: return 3 } @@ -70,12 +82,19 @@ func ClassifyTransport(addr ma.Multiaddr) TransportType { hasWS := hasProtocol(ma.P_WS) hasTLS := hasProtocol(ma.P_TLS) hasTCP := hasProtocol(ma.P_TCP) + hasWSS := hasProtocol(ma.P_WSS) // deprecated component used by libp2p live conns + hasQUICV1 := hasProtocol(ma.P_QUIC_V1) + hasQUIC := hasProtocol(ma.P_QUIC) switch { - case hasWS && hasTLS: + case hasWSS || hasWS && hasTLS: return TransportWSS case hasWS: return TransportWS + case hasQUICV1: + return TransportQUICV1 + case hasQUIC: + return TransportQUIC case hasTCP: return TransportTCP default: diff --git a/pkg/p2p/libp2p/metrics.go b/pkg/p2p/libp2p/metrics.go index 415ef0402f0..4bd6a1cc368 100644 --- a/pkg/p2p/libp2p/metrics.go +++ b/pkg/p2p/libp2p/metrics.go @@ -5,6 +5,8 @@ package libp2p import ( + "strings" + "github.com/ethersphere/bee/v2/pkg/bzz" m "github.com/ethersphere/bee/v2/pkg/metrics" ma "github.com/multiformats/go-multiaddr" @@ -14,7 +16,19 @@ import ( const ( connectionTransportLabelName = "transport" - connectionTransportHelp = "The 'transport' label is one of: tcp, ws, wss, quic-v1, quic, unknown." + connectionPublicLabelName = "public" +) + +var ( + transportLabelValues = []string{ + bzz.TransportTCP.String(), + bzz.TransportWS.String(), + bzz.TransportWSS.String(), + bzz.TransportQUICV1.String(), + bzz.TransportQUIC.String(), + bzz.TransportUnknown.String(), + } + publicLabelValues = []string{"true", "false"} ) type metrics struct { @@ -23,8 +37,6 @@ type metrics struct { // using reflection CreatedConnectionCount *prometheus.CounterVec HandledConnectionCount *prometheus.CounterVec - PublicAddressConnections *prometheus.CounterVec - PrivateAddressConnections *prometheus.CounterVec CreatedStreamCount prometheus.Counter ClosedStreamCount prometheus.Counter StreamResetCount prometheus.Counter @@ -41,44 +53,41 @@ type metrics struct { func newMetrics() metrics { subsystem := "libp2p" + transportHelp := "The 'transport' label is one of: " + strings.Join(transportLabelValues, ", ") + publicHelp := "The 'public' label is one of: " + strings.Join(publicLabelValues, ", ") + " (true = public remote multiaddr)." + + createdConnectionCount := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "created_connection_count", + Help: "Number of initiated outgoing libp2p connections. " + transportHelp, + }, + []string{connectionTransportLabelName}, + ) + + handledConnectionCount := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "handled_connection_count", + Help: "Number of handled incoming libp2p connections. " + transportHelp + " " + publicHelp, + }, + []string{connectionTransportLabelName, connectionPublicLabelName}, + ) + + // Ensure all expected label value combinations exist as 0-valued series, + // so Grafana shows a flat line instead of "No Data". + for _, transport := range transportLabelValues { + createdConnectionCount.WithLabelValues(transport).Add(0) + for _, public := range publicLabelValues { + handledConnectionCount.WithLabelValues(transport, public).Add(0) + } + } return metrics{ - CreatedConnectionCount: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "created_connection_count", - Help: "Number of initiated outgoing libp2p connections. " + connectionTransportHelp, - }, - []string{connectionTransportLabelName}, - ), - HandledConnectionCount: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "handled_connection_count", - Help: "Number of handled incoming libp2p connections. " + connectionTransportHelp, - }, - []string{connectionTransportLabelName}, - ), - PublicAddressConnections: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "public_address_connections_total", - Help: "Number of libp2p connections whose remote multiaddr is a public address. " + connectionTransportHelp, - }, - []string{connectionTransportLabelName}, - ), - PrivateAddressConnections: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: m.Namespace, - Subsystem: subsystem, - Name: "private_address_connections_total", - Help: "Number of libp2p connections whose remote multiaddr is a private address. " + connectionTransportHelp, - }, - []string{connectionTransportLabelName}, - ), + CreatedConnectionCount: createdConnectionCount, + HandledConnectionCount: handledConnectionCount, CreatedStreamCount: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: m.Namespace, Subsystem: subsystem, @@ -159,35 +168,15 @@ func (m metrics) incCreatedConnection(addr ma.Multiaddr) { } func (m metrics) observeHandledConnection(addr ma.Multiaddr) { - transport := connectionTransportLabel(addr) - m.HandledConnectionCount.WithLabelValues(transport).Inc() + public := "false" if manet.IsPublicAddr(addr) { - m.PublicAddressConnections.WithLabelValues(transport).Inc() - return + public = "true" } - m.PrivateAddressConnections.WithLabelValues(transport).Inc() + m.HandledConnectionCount.WithLabelValues(connectionTransportLabel(addr), public).Inc() } -// connectionTransportLabel returns the Prometheus transport label for a connection -// multiaddr. Live WSS connections are often encoded with the deprecated /wss -// component rather than the /tls/.../ws form used in advertised AutoTLS addresses. func connectionTransportLabel(addr ma.Multiaddr) string { - if addr == nil { - return bzz.TransportUnknown.String() - } - if _, err := addr.ValueForProtocol(ma.P_WSS); err == nil { - return bzz.TransportWSS.String() - } - if t := bzz.ClassifyTransport(addr); t != bzz.TransportUnknown { - return t.String() - } - if _, err := addr.ValueForProtocol(ma.P_QUIC_V1); err == nil { - return "quic-v1" - } - if _, err := addr.ValueForProtocol(ma.P_QUIC); err == nil { - return "quic" - } - return bzz.TransportUnknown.String() + return bzz.ClassifyTransport(addr).String() } func (s *Service) Metrics() []prometheus.Collector {