github.com/gogf/gf/v2@v2.7.4/net/gclient/gclient_observability.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 "context" 11 "fmt" 12 "net/http" 13 "net/http/httptrace" 14 15 "go.opentelemetry.io/otel" 16 "go.opentelemetry.io/otel/attribute" 17 "go.opentelemetry.io/otel/codes" 18 "go.opentelemetry.io/otel/propagation" 19 "go.opentelemetry.io/otel/trace" 20 21 "github.com/gogf/gf/v2" 22 "github.com/gogf/gf/v2/internal/httputil" 23 "github.com/gogf/gf/v2/net/gtrace" 24 "github.com/gogf/gf/v2/os/gctx" 25 "github.com/gogf/gf/v2/os/gmetric" 26 "github.com/gogf/gf/v2/util/gconv" 27 ) 28 29 const ( 30 instrumentName = "github.com/gogf/gf/v2/net/gclient.Client" 31 tracingAttrHttpAddressRemote = "http.address.remote" 32 tracingAttrHttpAddressLocal = "http.address.local" 33 tracingAttrHttpDnsStart = "http.dns.start" 34 tracingAttrHttpDnsDone = "http.dns.done" 35 tracingAttrHttpConnectStart = "http.connect.start" 36 tracingAttrHttpConnectDone = "http.connect.done" 37 tracingEventHttpRequest = "http.request" 38 tracingEventHttpRequestHeaders = "http.request.headers" 39 tracingEventHttpRequestBaggage = "http.request.baggage" 40 tracingEventHttpResponse = "http.response" 41 tracingEventHttpResponseHeaders = "http.response.headers" 42 tracingMiddlewareHandled gctx.StrKey = `MiddlewareClientTracingHandled` 43 ) 44 45 // internalMiddlewareObservability is a client middleware that enables observability feature. 46 func internalMiddlewareObservability(c *Client, r *http.Request) (response *Response, err error) { 47 var ctx = r.Context() 48 // Mark this request is handled by server tracing middleware, 49 // to avoid repeated handling by the same middleware. 50 if ctx.Value(tracingMiddlewareHandled) != nil { 51 return c.Next(r) 52 } 53 54 ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1) 55 tr := otel.GetTracerProvider().Tracer( 56 instrumentName, 57 trace.WithInstrumentationVersion(gf.VERSION), 58 ) 59 ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindClient)) 60 defer span.End() 61 62 span.SetAttributes(gtrace.CommonLabels()...) 63 64 // Inject tracing content into http header. 65 otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header)) 66 67 // Inject ClientTrace into context for http request. 68 var ( 69 httpClientTracer *httptrace.ClientTrace 70 baseClientTracer = newClientTracerNoop() 71 isUsingDefaultProvider = gtrace.IsUsingDefaultProvider() 72 ) 73 // Tracing. 74 if !isUsingDefaultProvider { 75 baseClientTracer = newClientTracerTracing(ctx, span, r) 76 } 77 // Metrics. 78 if gmetric.IsEnabled() { 79 baseClientTracer = newClientTracerMetrics(r, baseClientTracer) 80 } 81 httpClientTracer = newClientTracer(baseClientTracer) 82 r = r.WithContext( 83 httptrace.WithClientTrace( 84 ctx, httpClientTracer, 85 ), 86 ) 87 response, err = c.Next(r) 88 89 // If it is now using default trace provider, it then does no complex tracing jobs. 90 if isUsingDefaultProvider { 91 return 92 } 93 94 if err != nil { 95 span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) 96 } 97 if response == nil || response.Response == nil { 98 return 99 } 100 101 span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( 102 attribute.String( 103 tracingEventHttpResponseHeaders, 104 gconv.String(httputil.HeaderToMap(response.Header)), 105 ), 106 )) 107 return 108 }