package holepunch
import (
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/p2p/metricshelper"
ma "github.com/multiformats/go-multiaddr"
"github.com/prometheus/client_golang/prometheus"
)
const metricNamespace = "libp2p_holepunch"
var (
directDialsTotal = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "direct_dials_total" ,
Help : "Direct Dials Total" ,
},
[]string {"outcome" },
)
hpAddressOutcomesTotal = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "address_outcomes_total" ,
Help : "Hole Punch outcomes by Transport" ,
},
[]string {"side" , "num_attempts" , "ipv" , "transport" , "outcome" },
)
hpOutcomesTotal = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "outcomes_total" ,
Help : "Hole Punch outcomes overall" ,
},
[]string {"side" , "num_attempts" , "outcome" },
)
collectors = []prometheus .Collector {
directDialsTotal ,
hpAddressOutcomesTotal ,
hpOutcomesTotal ,
}
)
type MetricsTracer interface {
HolePunchFinished (side string , attemptNum int , theirAddrs []ma .Multiaddr , ourAddr []ma .Multiaddr , directConn network .ConnMultiaddrs )
DirectDialFinished (success bool )
}
type metricsTracer struct {}
var _ MetricsTracer = &metricsTracer {}
type metricsTracerSetting struct {
reg prometheus .Registerer
}
type MetricsTracerOption func (*metricsTracerSetting )
func WithRegisterer (reg prometheus .Registerer ) MetricsTracerOption {
return func (s *metricsTracerSetting ) {
if reg != nil {
s .reg = reg
}
}
}
func NewMetricsTracer (opts ...MetricsTracerOption ) MetricsTracer {
setting := &metricsTracerSetting {reg : prometheus .DefaultRegisterer }
for _ , opt := range opts {
opt (setting )
}
metricshelper .RegisterCollectors (setting .reg , collectors ...)
for _ , side := range []string {"initiator" , "receiver" } {
for _ , numAttempts := range []string {"1" , "2" , "3" , "4" } {
for _ , outcome := range []string {"success" , "failed" , "cancelled" , "no_suitable_address" } {
for _ , ipv := range []string {"ip4" , "ip6" } {
for _ , transport := range []string {"quic" , "quic-v1" , "tcp" , "webtransport" } {
hpAddressOutcomesTotal .WithLabelValues (side , numAttempts , ipv , transport , outcome )
}
}
if outcome == "cancelled" {
continue
}
hpOutcomesTotal .WithLabelValues (side , numAttempts , outcome )
}
}
}
return &metricsTracer {}
}
func (mt *metricsTracer ) HolePunchFinished (side string , numAttempts int ,
remoteAddrs []ma .Multiaddr , localAddrs []ma .Multiaddr , directConn network .ConnMultiaddrs ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = append (*tags , side , getNumAttemptString (numAttempts ))
var dipv , dtransport string
if directConn != nil {
dipv = metricshelper .GetIPVersion (directConn .LocalMultiaddr ())
dtransport = metricshelper .GetTransport (directConn .LocalMultiaddr ())
}
matchingAddressCount := 0
for _ , la := range localAddrs {
lipv := metricshelper .GetIPVersion (la )
ltransport := metricshelper .GetTransport (la )
matchingAddress := false
for _ , ra := range remoteAddrs {
ripv := metricshelper .GetIPVersion (ra )
rtransport := metricshelper .GetTransport (ra )
if ripv == lipv && rtransport == ltransport {
matchingAddress = true
matchingAddressCount ++
*tags = append (*tags , ripv , rtransport )
if directConn != nil && dipv == ripv && dtransport == rtransport {
*tags = append (*tags , "success" )
} else if directConn != nil {
*tags = append (*tags , "cancelled" )
} else {
*tags = append (*tags , "failed" )
}
hpAddressOutcomesTotal .WithLabelValues (*tags ...).Inc ()
*tags = (*tags )[:2 ]
break
}
}
if !matchingAddress {
*tags = append (*tags , lipv , ltransport , "no_suitable_address" )
hpAddressOutcomesTotal .WithLabelValues (*tags ...).Inc ()
*tags = (*tags )[:2 ]
}
}
outcome := "failed"
if directConn != nil {
outcome = "success"
} else if matchingAddressCount == 0 {
outcome = "no_suitable_address"
}
*tags = append (*tags , outcome )
hpOutcomesTotal .WithLabelValues (*tags ...).Inc ()
}
func getNumAttemptString(numAttempt int ) string {
var attemptStr = [...]string {"0" , "1" , "2" , "3" , "4" , "5" }
if numAttempt > 5 {
return "> 5"
}
return attemptStr [numAttempt ]
}
func (mt *metricsTracer ) DirectDialFinished (success bool ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
if success {
*tags = append (*tags , "success" )
} else {
*tags = append (*tags , "failed" )
}
directDialsTotal .WithLabelValues (*tags ...).Inc ()
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .