package swarm
import (
"context"
"errors"
"net"
"strings"
"time"
"github.com/libp2p/go-libp2p/core/crypto"
"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_swarm"
var (
connsOpened = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "connections_opened_total" ,
Help : "Connections Opened" ,
},
[]string {"dir" , "transport" , "security" , "muxer" , "early_muxer" , "ip_version" },
)
keyTypes = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "key_types_total" ,
Help : "key type" ,
},
[]string {"dir" , "key_type" },
)
connsClosed = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "connections_closed_total" ,
Help : "Connections Closed" ,
},
[]string {"dir" , "transport" , "security" , "muxer" , "early_muxer" , "ip_version" },
)
dialError = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "dial_errors_total" ,
Help : "Dial Error" ,
},
[]string {"transport" , "error" , "ip_version" },
)
connDuration = prometheus .NewHistogramVec (
prometheus .HistogramOpts {
Namespace : metricNamespace ,
Name : "connection_duration_seconds" ,
Help : "Duration of a Connection" ,
Buckets : prometheus .ExponentialBuckets (1.0 /16 , 2 , 25 ),
},
[]string {"dir" , "transport" , "security" , "muxer" , "early_muxer" , "ip_version" },
)
connHandshakeLatency = prometheus .NewHistogramVec (
prometheus .HistogramOpts {
Namespace : metricNamespace ,
Name : "handshake_latency_seconds" ,
Help : "Duration of the libp2p Handshake" ,
Buckets : prometheus .ExponentialBuckets (0.001 , 1.3 , 35 ),
},
[]string {"transport" , "security" , "muxer" , "early_muxer" , "ip_version" },
)
dialsPerPeer = prometheus .NewCounterVec (
prometheus .CounterOpts {
Namespace : metricNamespace ,
Name : "dials_per_peer_total" ,
Help : "Number of addresses dialed per peer" ,
},
[]string {"outcome" , "num_dials" },
)
dialLatency = prometheus .NewHistogramVec (
prometheus .HistogramOpts {
Namespace : metricNamespace ,
Name : "dial_latency_seconds" ,
Help : "time taken to establish connection with the peer" ,
Buckets : []float64 {0.001 , 0.01 , 0.05 , 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.75 , 1 , 2 },
},
[]string {"outcome" , "num_dials" },
)
dialRankingDelay = prometheus .NewHistogram (
prometheus .HistogramOpts {
Namespace : metricNamespace ,
Name : "dial_ranking_delay_seconds" ,
Help : "delay introduced by the dial ranking logic" ,
Buckets : []float64 {0.001 , 0.01 , 0.05 , 0.1 , 0.2 , 0.3 , 0.4 , 0.5 , 0.75 , 1 , 2 },
},
)
blackHoleSuccessCounterState = prometheus .NewGaugeVec (
prometheus .GaugeOpts {
Namespace : metricNamespace ,
Name : "black_hole_filter_state" ,
Help : "State of the black hole filter" ,
},
[]string {"name" },
)
blackHoleSuccessCounterSuccessFraction = prometheus .NewGaugeVec (
prometheus .GaugeOpts {
Namespace : metricNamespace ,
Name : "black_hole_filter_success_fraction" ,
Help : "Fraction of successful dials among the last n requests" ,
},
[]string {"name" },
)
blackHoleSuccessCounterNextRequestAllowedAfter = prometheus .NewGaugeVec (
prometheus .GaugeOpts {
Namespace : metricNamespace ,
Name : "black_hole_filter_next_request_allowed_after" ,
Help : "Number of requests after which the next request will be allowed" ,
},
[]string {"name" },
)
collectors = []prometheus .Collector {
connsOpened ,
keyTypes ,
connsClosed ,
dialError ,
connDuration ,
connHandshakeLatency ,
dialsPerPeer ,
dialRankingDelay ,
dialLatency ,
blackHoleSuccessCounterSuccessFraction ,
blackHoleSuccessCounterState ,
blackHoleSuccessCounterNextRequestAllowedAfter ,
}
)
type MetricsTracer interface {
OpenedConnection (network .Direction , crypto .PubKey , network .ConnectionState , ma .Multiaddr )
ClosedConnection (network .Direction , time .Duration , network .ConnectionState , ma .Multiaddr )
CompletedHandshake (time .Duration , network .ConnectionState , ma .Multiaddr )
FailedDialing (ma .Multiaddr , error , error )
DialCompleted (success bool , totalDials int , latency time .Duration )
DialRankingDelay (d time .Duration )
UpdatedBlackHoleSuccessCounter (name string , state BlackHoleState , nextProbeAfter int , successFraction float64 )
}
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 ...)
return &metricsTracer {}
}
func appendConnectionState(tags []string , cs network .ConnectionState ) []string {
if cs .Transport == "" {
tags = append (tags , "unknown" )
} else {
tags = append (tags , cs .Transport )
}
tags = append (tags , string (cs .Security ))
tags = append (tags , string (cs .StreamMultiplexer ))
earlyMuxer := "false"
if cs .UsedEarlyMuxerNegotiation {
earlyMuxer = "true"
}
tags = append (tags , earlyMuxer )
return tags
}
func (m *metricsTracer ) OpenedConnection (dir network .Direction , p crypto .PubKey , cs network .ConnectionState , laddr ma .Multiaddr ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = append (*tags , metricshelper .GetDirection (dir ))
*tags = appendConnectionState (*tags , cs )
*tags = append (*tags , metricshelper .GetIPVersion (laddr ))
connsOpened .WithLabelValues (*tags ...).Inc ()
*tags = (*tags )[:0 ]
*tags = append (*tags , metricshelper .GetDirection (dir ))
*tags = append (*tags , p .Type ().String ())
keyTypes .WithLabelValues (*tags ...).Inc ()
}
func (m *metricsTracer ) ClosedConnection (dir network .Direction , duration time .Duration , cs network .ConnectionState , laddr ma .Multiaddr ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = append (*tags , metricshelper .GetDirection (dir ))
*tags = appendConnectionState (*tags , cs )
*tags = append (*tags , metricshelper .GetIPVersion (laddr ))
connsClosed .WithLabelValues (*tags ...).Inc ()
connDuration .WithLabelValues (*tags ...).Observe (duration .Seconds ())
}
func (m *metricsTracer ) CompletedHandshake (t time .Duration , cs network .ConnectionState , laddr ma .Multiaddr ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = appendConnectionState (*tags , cs )
*tags = append (*tags , metricshelper .GetIPVersion (laddr ))
connHandshakeLatency .WithLabelValues (*tags ...).Observe (t .Seconds ())
}
func (m *metricsTracer ) FailedDialing (addr ma .Multiaddr , dialErr error , cause error ) {
transport := metricshelper .GetTransport (addr )
e := "other"
if errors .Is (dialErr , context .DeadlineExceeded ) || errors .Is (cause , context .DeadlineExceeded ) {
e = "deadline"
} else if errors .Is (dialErr , context .Canceled ) {
if errors .Is (cause , context .Canceled ) {
e = "application canceled"
} else if errors .Is (cause , errConcurrentDialSuccessful ) {
e = "canceled: concurrent dial successful"
} else {
e = "canceled: other"
}
} else {
nerr , ok := dialErr .(net .Error )
if ok && nerr .Timeout () {
e = "timeout"
} else if strings .Contains (dialErr .Error(), "connect: connection refused" ) {
e = "connection refused"
}
}
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = append (*tags , transport , e )
*tags = append (*tags , metricshelper .GetIPVersion (addr ))
dialError .WithLabelValues (*tags ...).Inc ()
}
func (m *metricsTracer ) DialCompleted (success bool , totalDials int , latency time .Duration ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
if success {
*tags = append (*tags , "success" )
} else {
*tags = append (*tags , "failed" )
}
numDialLabels := [...]string {"0" , "1" , "2" , "3" , "4" , "5" , ">=6" }
var numDials string
if totalDials < len (numDialLabels ) {
numDials = numDialLabels [totalDials ]
} else {
numDials = numDialLabels [len (numDialLabels )-1 ]
}
*tags = append (*tags , numDials )
dialsPerPeer .WithLabelValues (*tags ...).Inc ()
dialLatency .WithLabelValues (*tags ...).Observe (latency .Seconds ())
}
func (m *metricsTracer ) DialRankingDelay (d time .Duration ) {
dialRankingDelay .Observe (d .Seconds ())
}
func (m *metricsTracer ) UpdatedBlackHoleSuccessCounter (name string , state BlackHoleState ,
nextProbeAfter int , successFraction float64 ) {
tags := metricshelper .GetStringSlice ()
defer metricshelper .PutStringSlice (tags )
*tags = append (*tags , name )
blackHoleSuccessCounterState .WithLabelValues (*tags ...).Set (float64 (state ))
blackHoleSuccessCounterSuccessFraction .WithLabelValues (*tags ...).Set (successFraction )
blackHoleSuccessCounterNextRequestAllowedAfter .WithLabelValues (*tags ...).Set (float64 (nextProbeAfter ))
}
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 .