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  }