forked from quic-go/quic-go
metrics: add a very basic ConnectionTracer
This commit is contained in:
114
metrics/connection_tracer.go
Normal file
114
metrics/connection_tracer.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go/logging"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
connStarted = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: metricNamespace,
|
||||||
|
Name: "connections_started_total",
|
||||||
|
Help: "Connections Started",
|
||||||
|
},
|
||||||
|
[]string{"dir"},
|
||||||
|
)
|
||||||
|
connClosed = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: metricNamespace,
|
||||||
|
Name: "connections_closed_total",
|
||||||
|
Help: "Connections Closed",
|
||||||
|
},
|
||||||
|
[]string{"dir"},
|
||||||
|
)
|
||||||
|
connDuration = prometheus.NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Namespace: metricNamespace,
|
||||||
|
Name: "connection_duration_seconds",
|
||||||
|
Help: "Duration of a Connection",
|
||||||
|
Buckets: prometheus.ExponentialBuckets(1.0/16, 2, 25), // up to 24 days
|
||||||
|
},
|
||||||
|
[]string{"dir"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultTracer returns a callback that creates a metrics ConnectionTracer.
|
||||||
|
// The ConnectionTracer returned can be set on the quic.Config for a new connection.
|
||||||
|
// It should be reused across QUIC connections.
|
||||||
|
func DefaultTracer() func(_ context.Context, p logging.Perspective, _ logging.ConnectionID) *logging.ConnectionTracer {
|
||||||
|
return DefaultTracerWithRegisterer(prometheus.DefaultRegisterer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultTracerWithRegisterer returns a callback that creates a metrics ConnectionTracer
|
||||||
|
// using a given Prometheus registerer.
|
||||||
|
func DefaultTracerWithRegisterer(registerer prometheus.Registerer) func(_ context.Context, p logging.Perspective, _ logging.ConnectionID) *logging.ConnectionTracer {
|
||||||
|
return func(_ context.Context, p logging.Perspective, _ logging.ConnectionID) *logging.ConnectionTracer {
|
||||||
|
switch p {
|
||||||
|
case logging.PerspectiveClient:
|
||||||
|
return NewClientConnectionTracerWithRegisterer(registerer)
|
||||||
|
case logging.PerspectiveServer:
|
||||||
|
return NewServerConnectionTracerWithRegisterer(registerer)
|
||||||
|
default:
|
||||||
|
panic("invalid perspective")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientConnectionTracerWithRegisterer creates a new connection tracer for a connection
|
||||||
|
// dialed on the client side with a given Prometheus registerer.
|
||||||
|
func NewClientConnectionTracerWithRegisterer(registerer prometheus.Registerer) *logging.ConnectionTracer {
|
||||||
|
return newConnectionTracerWithRegisterer(registerer, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerConnectionTracerWithRegisterer creates a new connection tracer for a connection
|
||||||
|
// accepted on the server side with a given Prometheus registerer.
|
||||||
|
func NewServerConnectionTracerWithRegisterer(registerer prometheus.Registerer) *logging.ConnectionTracer {
|
||||||
|
return newConnectionTracerWithRegisterer(registerer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnectionTracerWithRegisterer(registerer prometheus.Registerer, isClient bool) *logging.ConnectionTracer {
|
||||||
|
for _, c := range [...]prometheus.Collector{
|
||||||
|
connStarted,
|
||||||
|
connClosed,
|
||||||
|
connDuration,
|
||||||
|
} {
|
||||||
|
if err := registerer.Register(c); err != nil {
|
||||||
|
if ok := errors.As(err, &prometheus.AlreadyRegisteredError{}); !ok {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
direction := "incoming"
|
||||||
|
if isClient {
|
||||||
|
direction = "outgoing"
|
||||||
|
}
|
||||||
|
|
||||||
|
var startTime time.Time
|
||||||
|
return &logging.ConnectionTracer{
|
||||||
|
StartedConnection: func(_, _ net.Addr, _, _ logging.ConnectionID) {
|
||||||
|
tags := getStringSlice()
|
||||||
|
defer putStringSlice(tags)
|
||||||
|
|
||||||
|
startTime = time.Now()
|
||||||
|
|
||||||
|
*tags = append(*tags, direction)
|
||||||
|
connStarted.WithLabelValues(*tags...).Inc()
|
||||||
|
},
|
||||||
|
ClosedConnection: func(_ error) {
|
||||||
|
tags := getStringSlice()
|
||||||
|
defer putStringSlice(tags)
|
||||||
|
|
||||||
|
*tags = append(*tags, direction)
|
||||||
|
connDuration.WithLabelValues(*tags...).Observe(time.Since(startTime).Seconds())
|
||||||
|
connClosed.WithLabelValues(*tags...).Inc()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,18 @@ quic.Transport{
|
|||||||
|
|
||||||
When using multiple `Transport`s, it is recommended to use the metrics tracer struct for all of them.
|
When using multiple `Transport`s, it is recommended to use the metrics tracer struct for all of them.
|
||||||
|
|
||||||
|
|
||||||
|
Set a metrics connection tracer on the `Config`:
|
||||||
|
```go
|
||||||
|
tracer := metrics.DefaultTracer()
|
||||||
|
quic.Config{
|
||||||
|
Tracer: tracer,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recommended to use the same connection tracer returned by `DefaultTracer` on the `Config`s for all connections.
|
||||||
|
|
||||||
|
|
||||||
Running:
|
Running:
|
||||||
```shell
|
```shell
|
||||||
docker-compose up
|
docker-compose up
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ func NewTracerWithRegisterer(registerer prometheus.Registerer) *logging.Tracer {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This should never happen. We only send Initials before creating the connection in order to
|
||||||
|
// reject a connection attempt.
|
||||||
|
if ccf == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if ccf.IsApplicationError {
|
if ccf.IsApplicationError {
|
||||||
//nolint:exhaustive // Only a few error codes applicable.
|
//nolint:exhaustive // Only a few error codes applicable.
|
||||||
switch qerr.TransportErrorCode(ccf.ErrorCode) {
|
switch qerr.TransportErrorCode(ccf.ErrorCode) {
|
||||||
|
|||||||
Reference in New Issue
Block a user