github.com/MetalBlockchain/metalgo@v1.11.9/utils/metric/api_interceptor.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package metric 5 6 import ( 7 "context" 8 "errors" 9 "net/http" 10 "time" 11 12 "github.com/gorilla/rpc/v2" 13 "github.com/prometheus/client_golang/prometheus" 14 ) 15 16 type APIInterceptor interface { 17 InterceptRequest(i *rpc.RequestInfo) *http.Request 18 AfterRequest(i *rpc.RequestInfo) 19 } 20 21 type contextKey int 22 23 const requestTimestampKey contextKey = iota 24 25 type apiInterceptor struct { 26 requestDurationCount *prometheus.CounterVec 27 requestDurationSum *prometheus.GaugeVec 28 requestErrors *prometheus.CounterVec 29 } 30 31 func NewAPIInterceptor(registerer prometheus.Registerer) (APIInterceptor, error) { 32 requestDurationCount := prometheus.NewCounterVec( 33 prometheus.CounterOpts{ 34 Name: "request_duration_count", 35 Help: "Number of times this type of request was made", 36 }, 37 []string{"method"}, 38 ) 39 requestDurationSum := prometheus.NewGaugeVec( 40 prometheus.GaugeOpts{ 41 Name: "request_duration_sum", 42 Help: "Amount of time in nanoseconds that has been spent handling this type of request", 43 }, 44 []string{"method"}, 45 ) 46 requestErrors := prometheus.NewCounterVec( 47 prometheus.CounterOpts{ 48 Name: "request_error_count", 49 }, 50 []string{"method"}, 51 ) 52 53 err := errors.Join( 54 registerer.Register(requestDurationCount), 55 registerer.Register(requestDurationSum), 56 registerer.Register(requestErrors), 57 ) 58 return &apiInterceptor{ 59 requestDurationCount: requestDurationCount, 60 requestDurationSum: requestDurationSum, 61 requestErrors: requestErrors, 62 }, err 63 } 64 65 func (*apiInterceptor) InterceptRequest(i *rpc.RequestInfo) *http.Request { 66 ctx := i.Request.Context() 67 ctx = context.WithValue(ctx, requestTimestampKey, time.Now()) 68 return i.Request.WithContext(ctx) 69 } 70 71 func (apr *apiInterceptor) AfterRequest(i *rpc.RequestInfo) { 72 timestampIntf := i.Request.Context().Value(requestTimestampKey) 73 timestamp, ok := timestampIntf.(time.Time) 74 if !ok { 75 return 76 } 77 78 durationMetricCount := apr.requestDurationCount.With(prometheus.Labels{ 79 "method": i.Method, 80 }) 81 durationMetricCount.Inc() 82 83 duration := time.Since(timestamp) 84 durationMetricSum := apr.requestDurationSum.With(prometheus.Labels{ 85 "method": i.Method, 86 }) 87 durationMetricSum.Add(float64(duration)) 88 89 if i.Error != nil { 90 errMetric := apr.requestErrors.With(prometheus.Labels{ 91 "method": i.Method, 92 }) 93 errMetric.Inc() 94 } 95 }