github.com/gogf/gf/v2@v2.7.4/net/gclient/gclient_metrics.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gclient
     8  
     9  import (
    10  	"net/http"
    11  
    12  	"github.com/gogf/gf/v2"
    13  	"github.com/gogf/gf/v2/os/gmetric"
    14  	"github.com/gogf/gf/v2/os/gtime"
    15  	"github.com/gogf/gf/v2/text/gstr"
    16  )
    17  
    18  type localMetricManager struct {
    19  	HttpClientRequestActive        gmetric.UpDownCounter
    20  	HttpClientRequestTotal         gmetric.Counter
    21  	HttpClientRequestDuration      gmetric.Histogram
    22  	HttpClientRequestDurationTotal gmetric.Counter
    23  	HttpClientConnectionDuration   gmetric.Histogram
    24  	HttpClientRequestBodySize      gmetric.Counter
    25  	HttpClientResponseBodySize     gmetric.Counter
    26  }
    27  
    28  const (
    29  	metricAttrKeyServerAddress          = "server.address"
    30  	metricAttrKeyServerPort             = "server.port"
    31  	metricAttrKeyUrlSchema              = "url.schema"
    32  	metricAttrKeyHttpRequestMethod      = "http.request.method"
    33  	metricAttrKeyHttpResponseStatusCode = "http.response.status_code"
    34  	metricAttrKeyNetworkProtocolVersion = "network.protocol.version"
    35  )
    36  
    37  var (
    38  	// metricManager for http client metrics.
    39  	metricManager = newMetricManager()
    40  )
    41  
    42  func newMetricManager() *localMetricManager {
    43  	meter := gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{
    44  		Instrument:        instrumentName,
    45  		InstrumentVersion: gf.VERSION,
    46  	})
    47  	durationBuckets := []float64{
    48  		1,
    49  		5,
    50  		10,
    51  		25,
    52  		50,
    53  		75,
    54  		100,
    55  		250,
    56  		500,
    57  		750,
    58  		1000,
    59  		2500,
    60  		5000,
    61  		7500,
    62  		10000,
    63  		30000,
    64  		60000,
    65  	}
    66  	mm := &localMetricManager{
    67  		HttpClientRequestDuration: meter.MustHistogram(
    68  			"http.client.request.duration",
    69  			gmetric.MetricOption{
    70  				Help:       "Measures the duration of client requests.",
    71  				Unit:       "ms",
    72  				Attributes: gmetric.Attributes{},
    73  				Buckets:    durationBuckets,
    74  			},
    75  		),
    76  		HttpClientRequestTotal: meter.MustCounter(
    77  			"http.client.request.total",
    78  			gmetric.MetricOption{
    79  				Help:       "Total processed request number.",
    80  				Unit:       "",
    81  				Attributes: gmetric.Attributes{},
    82  			},
    83  		),
    84  		HttpClientRequestActive: meter.MustUpDownCounter(
    85  			"http.client.request.active",
    86  			gmetric.MetricOption{
    87  				Help:       "Number of active client requests.",
    88  				Unit:       "",
    89  				Attributes: gmetric.Attributes{},
    90  			},
    91  		),
    92  		HttpClientRequestDurationTotal: meter.MustCounter(
    93  			"http.client.request.duration_total",
    94  			gmetric.MetricOption{
    95  				Help:       "Total execution duration of request.",
    96  				Unit:       "ms",
    97  				Attributes: gmetric.Attributes{},
    98  			},
    99  		),
   100  		HttpClientRequestBodySize: meter.MustCounter(
   101  			"http.client.request.body_size",
   102  			gmetric.MetricOption{
   103  				Help:       "Outgoing request bytes total.",
   104  				Unit:       "bytes",
   105  				Attributes: gmetric.Attributes{},
   106  			},
   107  		),
   108  		HttpClientResponseBodySize: meter.MustCounter(
   109  			"http.client.response.body_size",
   110  			gmetric.MetricOption{
   111  				Help:       "Response bytes total.",
   112  				Unit:       "bytes",
   113  				Attributes: gmetric.Attributes{},
   114  			},
   115  		),
   116  		HttpClientConnectionDuration: meter.MustHistogram(
   117  			"http.client.connection_duration",
   118  			gmetric.MetricOption{
   119  				Help:       "Measures the connection establish duration of client requests.",
   120  				Unit:       "ms",
   121  				Attributes: gmetric.Attributes{},
   122  				Buckets:    durationBuckets,
   123  			},
   124  		),
   125  	}
   126  	return mm
   127  }
   128  
   129  func (m *localMetricManager) GetMetricOptionForHistogram(r *http.Request) gmetric.Option {
   130  	attrMap := m.GetMetricAttributeMap(r)
   131  	return gmetric.Option{
   132  		Attributes: attrMap.Pick(
   133  			metricAttrKeyServerAddress,
   134  			metricAttrKeyServerPort,
   135  		),
   136  	}
   137  }
   138  
   139  func (m *localMetricManager) GetMetricOptionForHistogramByMap(attrMap gmetric.AttributeMap) gmetric.Option {
   140  	return gmetric.Option{
   141  		Attributes: attrMap.Pick(
   142  			metricAttrKeyServerAddress,
   143  			metricAttrKeyServerPort,
   144  		),
   145  	}
   146  }
   147  
   148  func (m *localMetricManager) GetMetricOptionForRequest(r *http.Request) gmetric.Option {
   149  	attrMap := m.GetMetricAttributeMap(r)
   150  	return m.GetMetricOptionForRequestByMap(attrMap)
   151  }
   152  
   153  func (m *localMetricManager) GetMetricOptionForRequestByMap(attrMap gmetric.AttributeMap) gmetric.Option {
   154  	return gmetric.Option{
   155  		Attributes: attrMap.Pick(
   156  			metricAttrKeyServerAddress,
   157  			metricAttrKeyServerPort,
   158  			metricAttrKeyHttpRequestMethod,
   159  			metricAttrKeyUrlSchema,
   160  			metricAttrKeyNetworkProtocolVersion,
   161  		),
   162  	}
   163  }
   164  
   165  func (m *localMetricManager) GetMetricOptionForResponseByMap(attrMap gmetric.AttributeMap) gmetric.Option {
   166  	return gmetric.Option{
   167  		Attributes: attrMap.Pick(
   168  			metricAttrKeyServerAddress,
   169  			metricAttrKeyServerPort,
   170  			metricAttrKeyHttpRequestMethod,
   171  			metricAttrKeyHttpResponseStatusCode,
   172  			metricAttrKeyUrlSchema,
   173  			metricAttrKeyNetworkProtocolVersion,
   174  		),
   175  	}
   176  }
   177  
   178  func (m *localMetricManager) GetMetricAttributeMap(r *http.Request) gmetric.AttributeMap {
   179  	var (
   180  		serverAddress   string
   181  		serverPort      string
   182  		protocolVersion string
   183  		attrMap         = make(gmetric.AttributeMap)
   184  	)
   185  	serverAddress, serverPort = gstr.List2(r.Host, ":")
   186  	if serverPort == "" {
   187  		_, serverPort = gstr.List2(r.RemoteAddr, ":")
   188  	}
   189  	if serverPort == "" {
   190  		serverPort = "80"
   191  		if r.URL.Scheme == "https" {
   192  			serverPort = "443"
   193  		}
   194  	}
   195  	if array := gstr.Split(r.Proto, "/"); len(array) > 1 {
   196  		protocolVersion = array[1]
   197  	}
   198  	attrMap.Sets(gmetric.AttributeMap{
   199  		metricAttrKeyServerAddress:          serverAddress,
   200  		metricAttrKeyServerPort:             serverPort,
   201  		metricAttrKeyUrlSchema:              r.URL.Scheme,
   202  		metricAttrKeyHttpRequestMethod:      r.Method,
   203  		metricAttrKeyNetworkProtocolVersion: protocolVersion,
   204  	})
   205  	if r.Response != nil {
   206  		attrMap.Sets(gmetric.AttributeMap{
   207  			metricAttrKeyHttpResponseStatusCode: r.Response.Status,
   208  		})
   209  	}
   210  	return attrMap
   211  }
   212  
   213  func (c *Client) handleMetricsBeforeRequest(r *http.Request) {
   214  	if !gmetric.IsEnabled() {
   215  		return
   216  	}
   217  
   218  	var (
   219  		ctx             = r.Context()
   220  		attrMap         = metricManager.GetMetricAttributeMap(r)
   221  		requestOption   = metricManager.GetMetricOptionForRequestByMap(attrMap)
   222  		requestBodySize = float64(r.ContentLength)
   223  	)
   224  	metricManager.HttpClientRequestActive.Inc(
   225  		ctx,
   226  		requestOption,
   227  	)
   228  	if requestBodySize > 0 {
   229  		metricManager.HttpClientRequestBodySize.Add(
   230  			ctx,
   231  			requestBodySize,
   232  			requestOption,
   233  		)
   234  	}
   235  }
   236  
   237  func (c *Client) handleMetricsAfterRequestDone(r *http.Request, requestStartTime *gtime.Time) {
   238  	if !gmetric.IsEnabled() {
   239  		return
   240  	}
   241  
   242  	var (
   243  		ctx             = r.Context()
   244  		attrMap         = metricManager.GetMetricAttributeMap(r)
   245  		duration        = float64(gtime.Now().Sub(requestStartTime).Milliseconds())
   246  		requestOption   = metricManager.GetMetricOptionForRequestByMap(attrMap)
   247  		responseOption  = metricManager.GetMetricOptionForResponseByMap(attrMap)
   248  		histogramOption = metricManager.GetMetricOptionForHistogramByMap(attrMap)
   249  	)
   250  	metricManager.HttpClientRequestActive.Dec(
   251  		ctx,
   252  		requestOption,
   253  	)
   254  	metricManager.HttpClientRequestTotal.Inc(
   255  		ctx,
   256  		responseOption,
   257  	)
   258  	metricManager.HttpClientRequestDuration.Record(
   259  		duration,
   260  		histogramOption,
   261  	)
   262  	metricManager.HttpClientRequestDurationTotal.Add(
   263  		ctx,
   264  		duration,
   265  		responseOption,
   266  	)
   267  	if r.Response != nil {
   268  		var responseBodySize = float64(r.Response.ContentLength)
   269  		if responseBodySize > 0 {
   270  			metricManager.HttpClientResponseBodySize.Add(
   271  				ctx,
   272  				responseBodySize,
   273  				responseOption,
   274  			)
   275  		}
   276  	}
   277  }