github.com/gogf/gf@v1.16.9/net/ghttp/internal/client/client_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/gogf/gf.
     6  
     7  package client
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/http/httptrace"
    14  
    15  	"github.com/gogf/gf"
    16  	"github.com/gogf/gf/internal/utils"
    17  	"github.com/gogf/gf/net/ghttp/internal/httputil"
    18  	"github.com/gogf/gf/net/gtrace"
    19  	"github.com/gogf/gf/text/gstr"
    20  	"github.com/gogf/gf/util/gconv"
    21  	"go.opentelemetry.io/otel"
    22  	"go.opentelemetry.io/otel/attribute"
    23  	"go.opentelemetry.io/otel/codes"
    24  	"go.opentelemetry.io/otel/propagation"
    25  	"go.opentelemetry.io/otel/trace"
    26  )
    27  
    28  const (
    29  	tracingInstrumentName           = "github.com/gogf/gf/net/ghttp.Client"
    30  	tracingAttrHttpAddressRemote    = "http.address.remote"
    31  	tracingAttrHttpAddressLocal     = "http.address.local"
    32  	tracingAttrHttpDnsStart         = "http.dns.start"
    33  	tracingAttrHttpDnsDone          = "http.dns.done"
    34  	tracingAttrHttpConnectStart     = "http.connect.start"
    35  	tracingAttrHttpConnectDone      = "http.connect.done"
    36  	tracingEventHttpRequest         = "http.request"
    37  	tracingEventHttpRequestHeaders  = "http.request.headers"
    38  	tracingEventHttpRequestBaggage  = "http.request.baggage"
    39  	tracingEventHttpRequestBody     = "http.request.body"
    40  	tracingEventHttpResponse        = "http.response"
    41  	tracingEventHttpResponseHeaders = "http.response.headers"
    42  	tracingEventHttpResponseBody    = "http.response.body"
    43  )
    44  
    45  // MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
    46  func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) {
    47  	tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
    48  	ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient))
    49  	defer span.End()
    50  
    51  	span.SetAttributes(gtrace.CommonLabels()...)
    52  
    53  	// Inject tracing content into http header.
    54  	otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header))
    55  
    56  	// Continue client handler executing.
    57  	response, err = c.Next(
    58  		r.WithContext(
    59  			httptrace.WithClientTrace(
    60  				ctx, newClientTrace(ctx, span, r),
    61  			),
    62  		),
    63  	)
    64  	if err != nil {
    65  		span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
    66  	}
    67  	if response == nil || response.Response == nil {
    68  		return
    69  	}
    70  
    71  	reqBodyContentBytes, _ := ioutil.ReadAll(response.Body)
    72  	response.Body = utils.NewReadCloser(reqBodyContentBytes, false)
    73  
    74  	span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
    75  		attribute.String(tracingEventHttpResponseHeaders, gconv.String(httputil.HeaderToMap(response.Header))),
    76  		attribute.String(tracingEventHttpResponseBody, gstr.StrLimit(
    77  			string(reqBodyContentBytes),
    78  			gtrace.MaxContentLogSize(),
    79  			"...",
    80  		)),
    81  	))
    82  	return
    83  }