github.com/livekit/protocol@v1.39.3/rpc/metrics.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package rpc 16 17 import ( 18 "sort" 19 sync "sync" 20 "time" 21 22 "github.com/prometheus/client_golang/prometheus" 23 "go.uber.org/atomic" 24 "golang.org/x/exp/maps" 25 26 "github.com/livekit/psrpc" 27 "github.com/livekit/psrpc/pkg/middleware" 28 ) 29 30 const ( 31 livekitNamespace = "livekit" 32 ) 33 34 type psrpcMetrics struct { 35 requestTime prometheus.ObserverVec 36 streamSendTime prometheus.ObserverVec 37 streamReceiveTotal *prometheus.CounterVec 38 streamCurrent *prometheus.GaugeVec 39 errorTotal *prometheus.CounterVec 40 bytesTotal *prometheus.CounterVec 41 } 42 43 var ( 44 metricsBase struct { 45 mu sync.RWMutex 46 initialized bool 47 curryLabels prometheus.Labels 48 psrpcMetrics 49 } 50 metrics atomic.Pointer[psrpcMetrics] 51 ) 52 53 type psrpcMetricsOptions struct { 54 curryLabels prometheus.Labels 55 } 56 57 type PSRPCMetricsOption func(*psrpcMetricsOptions) 58 59 func WithCurryLabels(labels prometheus.Labels) PSRPCMetricsOption { 60 return func(o *psrpcMetricsOptions) { 61 maps.Copy(o.curryLabels, labels) 62 } 63 } 64 65 func InitPSRPCStats(constLabels prometheus.Labels, opts ...PSRPCMetricsOption) { 66 metricsBase.mu.Lock() 67 if metricsBase.initialized { 68 metricsBase.mu.Unlock() 69 return 70 } 71 metricsBase.initialized = true 72 73 o := psrpcMetricsOptions{ 74 curryLabels: prometheus.Labels{}, 75 } 76 for _, opt := range opts { 77 opt(&o) 78 } 79 80 metricsBase.curryLabels = o.curryLabels 81 curryLabelNames := maps.Keys(o.curryLabels) 82 sort.Strings(curryLabelNames) 83 84 labels := append(curryLabelNames, "role", "kind", "service", "method") 85 streamLabels := append(curryLabelNames, "role", "service", "method") 86 bytesLabels := append(labels, "direction") 87 88 metricsBase.requestTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{ 89 Namespace: livekitNamespace, 90 Subsystem: "psrpc", 91 Name: "request_time_ms", 92 ConstLabels: constLabels, 93 Buckets: []float64{10, 50, 100, 300, 500, 1000, 1500, 2000, 5000, 10000}, 94 }, labels) 95 metricsBase.streamSendTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{ 96 Namespace: livekitNamespace, 97 Subsystem: "psrpc", 98 Name: "stream_send_time_ms", 99 ConstLabels: constLabels, 100 Buckets: []float64{10, 50, 100, 300, 500, 1000, 1500, 2000, 5000, 10000}, 101 }, streamLabels) 102 metricsBase.streamReceiveTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ 103 Namespace: livekitNamespace, 104 Subsystem: "psrpc", 105 Name: "stream_receive_total", 106 ConstLabels: constLabels, 107 }, streamLabels) 108 metricsBase.streamCurrent = prometheus.NewGaugeVec(prometheus.GaugeOpts{ 109 Namespace: livekitNamespace, 110 Subsystem: "psrpc", 111 Name: "stream_count", 112 ConstLabels: constLabels, 113 }, streamLabels) 114 metricsBase.errorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ 115 Namespace: livekitNamespace, 116 Subsystem: "psrpc", 117 Name: "error_total", 118 ConstLabels: constLabels, 119 }, labels) 120 metricsBase.bytesTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ 121 Namespace: livekitNamespace, 122 Subsystem: "psrpc", 123 Name: "bytes_total", 124 ConstLabels: constLabels, 125 }, bytesLabels) 126 127 metricsBase.mu.Unlock() 128 129 prometheus.MustRegister(metricsBase.requestTime) 130 prometheus.MustRegister(metricsBase.streamSendTime) 131 prometheus.MustRegister(metricsBase.streamReceiveTotal) 132 prometheus.MustRegister(metricsBase.streamCurrent) 133 prometheus.MustRegister(metricsBase.errorTotal) 134 prometheus.MustRegister(metricsBase.bytesTotal) 135 136 CurryMetricLabels(o.curryLabels) 137 } 138 139 func CurryMetricLabels(labels prometheus.Labels) { 140 metricsBase.mu.Lock() 141 defer metricsBase.mu.Unlock() 142 if !metricsBase.initialized { 143 return 144 } 145 146 for k := range metricsBase.curryLabels { 147 if v, ok := labels[k]; ok { 148 metricsBase.curryLabels[k] = v 149 } 150 } 151 152 metrics.Store(&psrpcMetrics{ 153 requestTime: metricsBase.requestTime.MustCurryWith(metricsBase.curryLabels), 154 streamSendTime: metricsBase.streamSendTime.MustCurryWith(metricsBase.curryLabels), 155 streamReceiveTotal: metricsBase.streamReceiveTotal.MustCurryWith(metricsBase.curryLabels), 156 streamCurrent: metricsBase.streamCurrent.MustCurryWith(metricsBase.curryLabels), 157 errorTotal: metricsBase.errorTotal.MustCurryWith(metricsBase.curryLabels), 158 bytesTotal: metricsBase.bytesTotal.MustCurryWith(metricsBase.curryLabels), 159 }) 160 } 161 162 var _ middleware.MetricsObserver = PSRPCMetricsObserver{} 163 164 type PSRPCMetricsObserver struct{} 165 166 func (o PSRPCMetricsObserver) OnUnaryRequest(role middleware.MetricRole, info psrpc.RPCInfo, duration time.Duration, err error, rxBytes, txBytes int) { 167 m := metrics.Load() 168 m.bytesTotal.WithLabelValues(role.String(), "rpc", info.Service, info.Method, "rx").Add(float64(rxBytes)) 169 m.bytesTotal.WithLabelValues(role.String(), "rpc", info.Service, info.Method, "tx").Add(float64(txBytes)) 170 171 if err != nil { 172 m.errorTotal.WithLabelValues(role.String(), "rpc", info.Service, info.Method).Inc() 173 } else { 174 m.requestTime.WithLabelValues(role.String(), "rpc", info.Service, info.Method).Observe(float64(duration.Milliseconds())) 175 } 176 } 177 178 func (o PSRPCMetricsObserver) OnMultiRequest(role middleware.MetricRole, info psrpc.RPCInfo, duration time.Duration, responseCount, errorCount, rxBytes, txBytes int) { 179 m := metrics.Load() 180 m.bytesTotal.WithLabelValues(role.String(), "multirpc", info.Service, info.Method, "rx").Add(float64(rxBytes)) 181 m.bytesTotal.WithLabelValues(role.String(), "multirpc", info.Service, info.Method, "tx").Add(float64(txBytes)) 182 183 if responseCount == 0 { 184 m.errorTotal.WithLabelValues(role.String(), "multirpc", info.Service, info.Method).Inc() 185 } else { 186 m.requestTime.WithLabelValues(role.String(), "multirpc", info.Service, info.Method).Observe(float64(duration.Milliseconds())) 187 } 188 } 189 190 func (o PSRPCMetricsObserver) OnStreamSend(role middleware.MetricRole, info psrpc.RPCInfo, duration time.Duration, err error, bytes int) { 191 m := metrics.Load() 192 m.bytesTotal.WithLabelValues(role.String(), "stream", info.Service, info.Method, "tx").Add(float64(bytes)) 193 194 if err != nil { 195 m.errorTotal.WithLabelValues(role.String(), "stream", info.Service, info.Method).Inc() 196 } else { 197 m.streamSendTime.WithLabelValues(role.String(), info.Service, info.Method).Observe(float64(duration.Milliseconds())) 198 } 199 } 200 201 func (o PSRPCMetricsObserver) OnStreamRecv(role middleware.MetricRole, info psrpc.RPCInfo, err error, bytes int) { 202 m := metrics.Load() 203 m.bytesTotal.WithLabelValues(role.String(), "stream", info.Service, info.Method, "rx").Add(float64(bytes)) 204 205 if err != nil { 206 m.errorTotal.WithLabelValues(role.String(), "stream", info.Service, info.Method).Inc() 207 } else { 208 m.streamReceiveTotal.WithLabelValues(role.String(), info.Service, info.Method).Inc() 209 } 210 } 211 212 func (o PSRPCMetricsObserver) OnStreamOpen(role middleware.MetricRole, info psrpc.RPCInfo) { 213 m := metrics.Load() 214 m.streamCurrent.WithLabelValues(role.String(), info.Service, info.Method).Inc() 215 } 216 217 func (o PSRPCMetricsObserver) OnStreamClose(role middleware.MetricRole, info psrpc.RPCInfo) { 218 m := metrics.Load() 219 m.streamCurrent.WithLabelValues(role.String(), info.Service, info.Method).Dec() 220 } 221 222 var _ middleware.MetricsObserver = UnimplementedMetricsObserver{} 223 224 type UnimplementedMetricsObserver struct{} 225 226 func (o UnimplementedMetricsObserver) OnUnaryRequest(role middleware.MetricRole, rpcInfo psrpc.RPCInfo, duration time.Duration, err error, rxBytes, txBytes int) { 227 } 228 func (o UnimplementedMetricsObserver) OnMultiRequest(role middleware.MetricRole, rpcInfo psrpc.RPCInfo, duration time.Duration, responseCount, errorCount, reqBytes, txBytes int) { 229 } 230 func (o UnimplementedMetricsObserver) OnStreamSend(role middleware.MetricRole, rpcInfo psrpc.RPCInfo, duration time.Duration, err error, bytes int) { 231 } 232 func (o UnimplementedMetricsObserver) OnStreamRecv(role middleware.MetricRole, rpcInfo psrpc.RPCInfo, err error, bytes int) { 233 } 234 func (o UnimplementedMetricsObserver) OnStreamOpen(role middleware.MetricRole, rpcInfo psrpc.RPCInfo) { 235 } 236 func (o UnimplementedMetricsObserver) OnStreamClose(role middleware.MetricRole, rpcInfo psrpc.RPCInfo) { 237 }