metrics.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package quic
  2. import (
  3. "reflect"
  4. "strings"
  5. "sync"
  6. "github.com/prometheus/client_golang/prometheus"
  7. "github.com/quic-go/quic-go/logging"
  8. "github.com/rs/zerolog"
  9. )
  10. const (
  11. namespace = "quic"
  12. )
  13. var (
  14. clientConnLabels = []string{"conn_index"}
  15. clientMetrics = struct {
  16. totalConnections prometheus.Counter
  17. closedConnections prometheus.Counter
  18. maxUDPPayloadSize *prometheus.GaugeVec
  19. sentFrames *prometheus.CounterVec
  20. sentBytes *prometheus.CounterVec
  21. receivedFrames *prometheus.CounterVec
  22. receivedBytes *prometheus.CounterVec
  23. bufferedPackets *prometheus.CounterVec
  24. droppedPackets *prometheus.CounterVec
  25. lostPackets *prometheus.CounterVec
  26. minRTT *prometheus.GaugeVec
  27. latestRTT *prometheus.GaugeVec
  28. smoothedRTT *prometheus.GaugeVec
  29. mtu *prometheus.GaugeVec
  30. congestionWindow *prometheus.GaugeVec
  31. congestionState *prometheus.GaugeVec
  32. }{
  33. totalConnections: prometheus.NewCounter(
  34. prometheus.CounterOpts{
  35. Namespace: namespace,
  36. Subsystem: "client",
  37. Name: "total_connections",
  38. Help: "Number of connections initiated",
  39. },
  40. ),
  41. closedConnections: prometheus.NewCounter(
  42. prometheus.CounterOpts{
  43. Namespace: namespace,
  44. Subsystem: "client",
  45. Name: "closed_connections",
  46. Help: "Number of connections that has been closed",
  47. },
  48. ),
  49. maxUDPPayloadSize: prometheus.NewGaugeVec(
  50. prometheus.GaugeOpts{
  51. Namespace: namespace,
  52. Subsystem: "client",
  53. Name: "max_udp_payload",
  54. Help: "Maximum UDP payload size in bytes for a QUIC packet",
  55. },
  56. clientConnLabels,
  57. ),
  58. sentFrames: prometheus.NewCounterVec(
  59. prometheus.CounterOpts{
  60. Namespace: namespace,
  61. Subsystem: "client",
  62. Name: "sent_frames",
  63. Help: "Number of frames that have been sent through a connection",
  64. },
  65. append(clientConnLabels, "frame_type"),
  66. ),
  67. sentBytes: prometheus.NewCounterVec(
  68. prometheus.CounterOpts{
  69. Namespace: namespace,
  70. Subsystem: "client",
  71. Name: "sent_bytes",
  72. Help: "Number of bytes that have been sent through a connection",
  73. },
  74. clientConnLabels,
  75. ),
  76. receivedFrames: prometheus.NewCounterVec(
  77. prometheus.CounterOpts{
  78. Namespace: namespace,
  79. Subsystem: "client",
  80. Name: "received_frames",
  81. Help: "Number of frames that have been received through a connection",
  82. },
  83. append(clientConnLabels, "frame_type"),
  84. ),
  85. receivedBytes: prometheus.NewCounterVec(
  86. prometheus.CounterOpts{
  87. Namespace: namespace,
  88. Subsystem: "client",
  89. Name: "receive_bytes",
  90. Help: "Number of bytes that have been received through a connection",
  91. },
  92. clientConnLabels,
  93. ),
  94. bufferedPackets: prometheus.NewCounterVec(
  95. prometheus.CounterOpts{
  96. Namespace: namespace,
  97. Subsystem: "client",
  98. Name: "buffered_packets",
  99. Help: "Number of bytes that have been buffered on a connection",
  100. },
  101. append(clientConnLabels, "packet_type"),
  102. ),
  103. droppedPackets: prometheus.NewCounterVec(
  104. prometheus.CounterOpts{
  105. Namespace: namespace,
  106. Subsystem: "client",
  107. Name: "dropped_packets",
  108. Help: "Number of bytes that have been dropped on a connection",
  109. },
  110. append(clientConnLabels, "packet_type", "reason"),
  111. ),
  112. lostPackets: prometheus.NewCounterVec(
  113. prometheus.CounterOpts{
  114. Namespace: namespace,
  115. Subsystem: "client",
  116. Name: "lost_packets",
  117. Help: "Number of packets that have been lost from a connection",
  118. },
  119. append(clientConnLabels, "reason"),
  120. ),
  121. minRTT: prometheus.NewGaugeVec(
  122. prometheus.GaugeOpts{
  123. Namespace: namespace,
  124. Subsystem: "client",
  125. Name: "min_rtt",
  126. Help: "Lowest RTT measured on a connection in millisec",
  127. },
  128. clientConnLabels,
  129. ),
  130. latestRTT: prometheus.NewGaugeVec(
  131. prometheus.GaugeOpts{
  132. Namespace: namespace,
  133. Subsystem: "client",
  134. Name: "latest_rtt",
  135. Help: "Latest RTT measured on a connection",
  136. },
  137. clientConnLabels,
  138. ),
  139. smoothedRTT: prometheus.NewGaugeVec(
  140. prometheus.GaugeOpts{
  141. Namespace: namespace,
  142. Subsystem: "client",
  143. Name: "smoothed_rtt",
  144. Help: "Calculated smoothed RTT measured on a connection in millisec",
  145. },
  146. clientConnLabels,
  147. ),
  148. mtu: prometheus.NewGaugeVec(
  149. prometheus.GaugeOpts{
  150. Namespace: namespace,
  151. Subsystem: "client",
  152. Name: "mtu",
  153. Help: "Current maximum transmission unit (MTU) of a connection",
  154. },
  155. clientConnLabels,
  156. ),
  157. congestionWindow: prometheus.NewGaugeVec(
  158. prometheus.GaugeOpts{
  159. Namespace: namespace,
  160. Subsystem: "client",
  161. Name: "congestion_window",
  162. Help: "Current congestion window size",
  163. },
  164. clientConnLabels,
  165. ),
  166. congestionState: prometheus.NewGaugeVec(
  167. prometheus.GaugeOpts{
  168. Namespace: namespace,
  169. Subsystem: "client",
  170. Name: "congestion_state",
  171. Help: "Current congestion control state. See https://pkg.go.dev/github.com/quic-go/quic-go@v0.45.0/logging#CongestionState for what each value maps to",
  172. },
  173. clientConnLabels,
  174. ),
  175. }
  176. registerClient = sync.Once{}
  177. packetTooBigDropped = prometheus.NewCounter(prometheus.CounterOpts{
  178. Namespace: namespace,
  179. Subsystem: "client",
  180. Name: "packet_too_big_dropped",
  181. Help: "Count of packets received from origin that are too big to send to the edge and are dropped as a result",
  182. })
  183. )
  184. type clientCollector struct {
  185. index string
  186. logger *zerolog.Logger
  187. }
  188. func newClientCollector(index string, logger *zerolog.Logger) *clientCollector {
  189. registerClient.Do(func() {
  190. prometheus.MustRegister(
  191. clientMetrics.totalConnections,
  192. clientMetrics.closedConnections,
  193. clientMetrics.maxUDPPayloadSize,
  194. clientMetrics.sentFrames,
  195. clientMetrics.sentBytes,
  196. clientMetrics.receivedFrames,
  197. clientMetrics.receivedBytes,
  198. clientMetrics.bufferedPackets,
  199. clientMetrics.droppedPackets,
  200. clientMetrics.lostPackets,
  201. clientMetrics.minRTT,
  202. clientMetrics.latestRTT,
  203. clientMetrics.smoothedRTT,
  204. clientMetrics.mtu,
  205. clientMetrics.congestionWindow,
  206. clientMetrics.congestionState,
  207. packetTooBigDropped,
  208. )
  209. })
  210. return &clientCollector{
  211. index: index,
  212. logger: logger,
  213. }
  214. }
  215. func (cc *clientCollector) startedConnection() {
  216. clientMetrics.totalConnections.Inc()
  217. }
  218. func (cc *clientCollector) closedConnection(error) {
  219. clientMetrics.closedConnections.Inc()
  220. }
  221. func (cc *clientCollector) receivedTransportParameters(params *logging.TransportParameters) {
  222. clientMetrics.maxUDPPayloadSize.WithLabelValues(cc.index).Set(float64(params.MaxUDPPayloadSize))
  223. cc.logger.Debug().Msgf("Received transport parameters: MaxUDPPayloadSize=%d, MaxIdleTimeout=%v, MaxDatagramFrameSize=%d", params.MaxUDPPayloadSize, params.MaxIdleTimeout, params.MaxDatagramFrameSize)
  224. }
  225. func (cc *clientCollector) sentPackets(size logging.ByteCount, frames []logging.Frame) {
  226. cc.collectPackets(size, frames, clientMetrics.sentFrames, clientMetrics.sentBytes, sent)
  227. }
  228. func (cc *clientCollector) receivedPackets(size logging.ByteCount, frames []logging.Frame) {
  229. cc.collectPackets(size, frames, clientMetrics.receivedFrames, clientMetrics.receivedBytes, received)
  230. }
  231. func (cc *clientCollector) bufferedPackets(packetType logging.PacketType) {
  232. clientMetrics.bufferedPackets.WithLabelValues(cc.index, packetTypeString(packetType)).Inc()
  233. }
  234. func (cc *clientCollector) droppedPackets(packetType logging.PacketType, size logging.ByteCount, reason logging.PacketDropReason) {
  235. clientMetrics.droppedPackets.WithLabelValues(
  236. cc.index,
  237. packetTypeString(packetType),
  238. packetDropReasonString(reason),
  239. ).Add(byteCountToPromCount(size))
  240. }
  241. func (cc *clientCollector) lostPackets(reason logging.PacketLossReason) {
  242. clientMetrics.lostPackets.WithLabelValues(cc.index, packetLossReasonString(reason)).Inc()
  243. }
  244. func (cc *clientCollector) updatedRTT(rtt *logging.RTTStats) {
  245. clientMetrics.minRTT.WithLabelValues(cc.index).Set(durationToPromGauge(rtt.MinRTT()))
  246. clientMetrics.latestRTT.WithLabelValues(cc.index).Set(durationToPromGauge(rtt.LatestRTT()))
  247. clientMetrics.smoothedRTT.WithLabelValues(cc.index).Set(durationToPromGauge(rtt.SmoothedRTT()))
  248. }
  249. func (cc *clientCollector) updateCongestionWindow(size logging.ByteCount) {
  250. clientMetrics.congestionWindow.WithLabelValues(cc.index).Set(float64(size))
  251. }
  252. func (cc *clientCollector) updatedCongestionState(state logging.CongestionState) {
  253. clientMetrics.congestionState.WithLabelValues(cc.index).Set(float64(state))
  254. }
  255. func (cc *clientCollector) updateMTU(mtu logging.ByteCount) {
  256. clientMetrics.mtu.WithLabelValues(cc.index).Set(float64(mtu))
  257. cc.logger.Debug().Msgf("QUIC MTU updated to %d", mtu)
  258. }
  259. func (cc *clientCollector) collectPackets(size logging.ByteCount, frames []logging.Frame, counter, bandwidth *prometheus.CounterVec, direction direction) {
  260. for _, frame := range frames {
  261. switch f := frame.(type) {
  262. case logging.DataBlockedFrame:
  263. cc.logger.Debug().Msgf("%s data_blocked frame", direction)
  264. case logging.StreamDataBlockedFrame:
  265. cc.logger.Debug().Int64("streamID", int64(f.StreamID)).Msgf("%s stream_data_blocked frame", direction)
  266. }
  267. counter.WithLabelValues(cc.index, frameName(frame)).Inc()
  268. }
  269. bandwidth.WithLabelValues(cc.index).Add(byteCountToPromCount(size))
  270. }
  271. func frameName(frame logging.Frame) string {
  272. if frame == nil {
  273. return "nil"
  274. } else {
  275. name := reflect.TypeOf(frame).Elem().Name()
  276. return strings.TrimSuffix(name, "Frame")
  277. }
  278. }
  279. type direction uint8
  280. const (
  281. sent direction = iota
  282. received
  283. )
  284. func (d direction) String() string {
  285. if d == sent {
  286. return "sent"
  287. }
  288. return "received"
  289. }