forked from quic-go/quic-go
148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
package metrics
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/quic-go/quic-go/internal/protocol"
|
|
"github.com/quic-go/quic-go/internal/qerr"
|
|
"github.com/quic-go/quic-go/logging"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
const metricNamespace = "quicgo"
|
|
|
|
func getIPVersion(addr net.Addr) string {
|
|
udpAddr, ok := addr.(*net.UDPAddr)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
if udpAddr.IP.To4() != nil {
|
|
return "ipv4"
|
|
}
|
|
return "ipv6"
|
|
}
|
|
|
|
var (
|
|
connsRejected = prometheus.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Namespace: metricNamespace,
|
|
Name: "server_connections_rejected_total",
|
|
Help: "Connections Rejected",
|
|
},
|
|
[]string{"ip_version", "reason"},
|
|
)
|
|
packetDropped = prometheus.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Namespace: metricNamespace,
|
|
Name: "server_received_packets_dropped_total",
|
|
Help: "packets dropped",
|
|
},
|
|
[]string{"ip_version", "reason"},
|
|
)
|
|
)
|
|
|
|
// NewTracer creates a new tracer using the default Prometheus registerer.
|
|
// The Tracer returned from this function can be used to collect metrics for
|
|
// events happening before the establishment of a QUIC connection.
|
|
// It can be set on the Tracer field of quic.Transport.
|
|
func NewTracer() *logging.Tracer {
|
|
return NewTracerWithRegisterer(prometheus.DefaultRegisterer)
|
|
}
|
|
|
|
// NewTracerWithRegisterer creates a new tracer using a given Prometheus registerer.
|
|
func NewTracerWithRegisterer(registerer prometheus.Registerer) *logging.Tracer {
|
|
for _, c := range [...]prometheus.Collector{
|
|
connsRejected,
|
|
packetDropped,
|
|
} {
|
|
if err := registerer.Register(c); err != nil {
|
|
if ok := errors.As(err, &prometheus.AlreadyRegisteredError{}); !ok {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &logging.Tracer{
|
|
SentPacket: func(addr net.Addr, hdr *logging.Header, _ logging.ByteCount, frames []logging.Frame) {
|
|
tags := getStringSlice()
|
|
defer putStringSlice(tags)
|
|
|
|
var reason string
|
|
switch {
|
|
case hdr.Type == protocol.PacketTypeRetry:
|
|
reason = "retry"
|
|
case hdr.Type == protocol.PacketTypeInitial:
|
|
var ccf *logging.ConnectionCloseFrame
|
|
for _, f := range frames {
|
|
cc, ok := f.(*logging.ConnectionCloseFrame)
|
|
if ok {
|
|
ccf = cc
|
|
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 {
|
|
//nolint:exhaustive // Only a few error codes applicable.
|
|
switch qerr.TransportErrorCode(ccf.ErrorCode) {
|
|
case qerr.ConnectionRefused:
|
|
reason = "connection_refused"
|
|
case qerr.InvalidToken:
|
|
reason = "invalid_token"
|
|
default:
|
|
// This shouldn't happen, the server doesn't send CONNECTION_CLOSE frames with different errors.
|
|
reason = fmt.Sprintf("transport_error: %d", ccf.ErrorCode)
|
|
}
|
|
} else {
|
|
// This shouldn't happen, the server doesn't send application-level CONNECTION_CLOSE frames.
|
|
reason = "application_error"
|
|
}
|
|
}
|
|
*tags = append(*tags, getIPVersion(addr))
|
|
*tags = append(*tags, reason)
|
|
connsRejected.WithLabelValues(*tags...).Inc()
|
|
},
|
|
SentVersionNegotiationPacket: func(addr net.Addr, _, _ logging.ArbitraryLenConnectionID, _ []logging.Version) {
|
|
tags := getStringSlice()
|
|
defer putStringSlice(tags)
|
|
|
|
*tags = append(*tags, getIPVersion(addr))
|
|
*tags = append(*tags, "version_negotiation")
|
|
connsRejected.WithLabelValues(*tags...).Inc()
|
|
},
|
|
DroppedPacket: func(addr net.Addr, pt logging.PacketType, _ logging.ByteCount, reason logging.PacketDropReason) {
|
|
tags := getStringSlice()
|
|
defer putStringSlice(tags)
|
|
|
|
var dropReason string
|
|
//nolint:exhaustive // Only a few drop reasons applicable.
|
|
switch reason {
|
|
case logging.PacketDropDOSPrevention:
|
|
if pt == logging.PacketType0RTT {
|
|
dropReason = "0rtt_dos_prevention"
|
|
} else {
|
|
dropReason = "dos_prevention"
|
|
}
|
|
case logging.PacketDropHeaderParseError:
|
|
dropReason = "header_parsing"
|
|
case logging.PacketDropPayloadDecryptError:
|
|
dropReason = "payload_decrypt"
|
|
case logging.PacketDropUnexpectedPacket:
|
|
dropReason = "unexpected_packet"
|
|
default:
|
|
dropReason = "unknown"
|
|
}
|
|
|
|
*tags = append(*tags, getIPVersion(addr))
|
|
*tags = append(*tags, dropReason)
|
|
packetDropped.WithLabelValues(*tags...).Inc()
|
|
},
|
|
}
|
|
}
|