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