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 }