github.com/gogf/gf/v2@v2.7.4/net/gclient/gclient_tracer_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 gclient
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"io"
    14  	"net/http"
    15  	"net/http/httptrace"
    16  	"net/textproto"
    17  	"strings"
    18  	"sync"
    19  
    20  	"go.opentelemetry.io/otel/attribute"
    21  	"go.opentelemetry.io/otel/codes"
    22  	"go.opentelemetry.io/otel/trace"
    23  
    24  	"github.com/gogf/gf/v2/internal/utils"
    25  	"github.com/gogf/gf/v2/net/gtrace"
    26  	"github.com/gogf/gf/v2/util/gconv"
    27  )
    28  
    29  // clientTracerTracing is used for implementing httptrace.ClientTrace.
    30  type clientTracerTracing struct {
    31  	context.Context
    32  	span        trace.Span
    33  	request     *http.Request
    34  	requestBody []byte
    35  	headers     map[string]interface{}
    36  	mtx         sync.Mutex
    37  }
    38  
    39  // newClientTracerTracing creates and returns object of httptrace.ClientTrace.
    40  func newClientTracerTracing(
    41  	ctx context.Context,
    42  	span trace.Span,
    43  	request *http.Request,
    44  ) *httptrace.ClientTrace {
    45  	ct := &clientTracerTracing{
    46  		Context: ctx,
    47  		span:    span,
    48  		request: request,
    49  		headers: make(map[string]interface{}),
    50  	}
    51  
    52  	reqBodyContent, _ := io.ReadAll(ct.request.Body)
    53  	ct.requestBody = reqBodyContent
    54  	ct.request.Body = utils.NewReadCloser(reqBodyContent, false)
    55  
    56  	return &httptrace.ClientTrace{
    57  		GetConn:              ct.GetConn,
    58  		GotConn:              ct.GotConn,
    59  		PutIdleConn:          ct.PutIdleConn,
    60  		GotFirstResponseByte: ct.GotFirstResponseByte,
    61  		Got100Continue:       ct.Got100Continue,
    62  		Got1xxResponse:       ct.Got1xxResponse,
    63  		DNSStart:             ct.DNSStart,
    64  		DNSDone:              ct.DNSDone,
    65  		ConnectStart:         ct.ConnectStart,
    66  		ConnectDone:          ct.ConnectDone,
    67  		TLSHandshakeStart:    ct.TLSHandshakeStart,
    68  		TLSHandshakeDone:     ct.TLSHandshakeDone,
    69  		WroteHeaderField:     ct.WroteHeaderField,
    70  		WroteHeaders:         ct.WroteHeaders,
    71  		Wait100Continue:      ct.Wait100Continue,
    72  		WroteRequest:         ct.WroteRequest,
    73  	}
    74  }
    75  
    76  // GetConn is called before a connection is created or
    77  // retrieved from an idle pool. The hostPort is the
    78  // "host:port" of the target or proxy. GetConn is called even
    79  // if there's already an idle cached connection available.
    80  func (ct *clientTracerTracing) GetConn(host string) {}
    81  
    82  // GotConn is called after a successful connection is
    83  // obtained. There is no hook for failure to obtain a
    84  // connection; instead, use the error from
    85  // Transport.RoundTrip.
    86  func (ct *clientTracerTracing) GotConn(info httptrace.GotConnInfo) {
    87  	remoteAddr := ""
    88  	if info.Conn.RemoteAddr() != nil {
    89  		remoteAddr = info.Conn.RemoteAddr().String()
    90  	}
    91  	localAddr := ""
    92  	if info.Conn.LocalAddr() != nil {
    93  		localAddr = info.Conn.LocalAddr().String()
    94  	}
    95  	ct.span.SetAttributes(
    96  		attribute.String(tracingAttrHttpAddressRemote, remoteAddr),
    97  		attribute.String(tracingAttrHttpAddressLocal, localAddr),
    98  	)
    99  }
   100  
   101  // PutIdleConn is called when the connection is returned to
   102  // the idle pool. If err is nil, the connection was
   103  // successfully returned to the idle pool. If err is non-nil,
   104  // it describes why not. PutIdleConn is not called if
   105  // connection reuse is disabled via Transport.DisableKeepAlives.
   106  // PutIdleConn is called before the caller's Response.Body.Close
   107  // call returns.
   108  // For HTTP/2, this hook is not currently used.
   109  func (ct *clientTracerTracing) PutIdleConn(err error) {
   110  	if err != nil {
   111  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   112  	}
   113  }
   114  
   115  // GotFirstResponseByte is called when the first byte of the response
   116  // headers is available.
   117  func (ct *clientTracerTracing) GotFirstResponseByte() {}
   118  
   119  // Got100Continue is called if the server replies with a "100
   120  // Continue" response.
   121  func (ct *clientTracerTracing) Got100Continue() {}
   122  
   123  // Got1xxResponse is called for each 1xx informational response header
   124  // returned before the final non-1xx response. Got1xxResponse is called
   125  // for "100 Continue" responses, even if Got100Continue is also defined.
   126  // If it returns an error, the client request is aborted with that error value.
   127  func (ct *clientTracerTracing) Got1xxResponse(code int, header textproto.MIMEHeader) error {
   128  	return nil
   129  }
   130  
   131  // DNSStart is called when a DNS lookup begins.
   132  func (ct *clientTracerTracing) DNSStart(info httptrace.DNSStartInfo) {
   133  	ct.span.SetAttributes(
   134  		attribute.String(tracingAttrHttpDnsStart, info.Host),
   135  	)
   136  }
   137  
   138  // DNSDone is called when a DNS lookup ends.
   139  func (ct *clientTracerTracing) DNSDone(info httptrace.DNSDoneInfo) {
   140  	var buffer strings.Builder
   141  	for _, v := range info.Addrs {
   142  		if buffer.Len() != 0 {
   143  			buffer.WriteString(",")
   144  		}
   145  		buffer.WriteString(v.String())
   146  	}
   147  	if info.Err != nil {
   148  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
   149  	}
   150  	ct.span.SetAttributes(
   151  		attribute.String(tracingAttrHttpDnsDone, buffer.String()),
   152  	)
   153  }
   154  
   155  // ConnectStart is called when a new connection's Dial begins.
   156  // If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
   157  // enabled, this may be called multiple times.
   158  func (ct *clientTracerTracing) ConnectStart(network, addr string) {
   159  	ct.span.SetAttributes(
   160  		attribute.String(tracingAttrHttpConnectStart, network+"@"+addr),
   161  	)
   162  }
   163  
   164  // ConnectDone is called when a new connection's Dial
   165  // completes. The provided err indicates whether the
   166  // connection completed successfully.
   167  // If net.Dialer.DualStack ("Happy Eyeballs") support is
   168  // enabled, this may be called multiple times.
   169  func (ct *clientTracerTracing) ConnectDone(network, addr string, err error) {
   170  	if err != nil {
   171  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   172  	}
   173  	ct.span.SetAttributes(
   174  		attribute.String(tracingAttrHttpConnectDone, network+"@"+addr),
   175  	)
   176  }
   177  
   178  // TLSHandshakeStart is called when the TLS handshake is started. When
   179  // connecting to an HTTPS site via an HTTP proxy, the handshake happens
   180  // after the CONNECT request is processed by the proxy.
   181  func (ct *clientTracerTracing) TLSHandshakeStart() {}
   182  
   183  // TLSHandshakeDone is called after the TLS handshake with either the
   184  // successful handshake's connection state, or a non-nil error on handshake
   185  // failure.
   186  func (ct *clientTracerTracing) TLSHandshakeDone(_ tls.ConnectionState, err error) {
   187  	if err != nil {
   188  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   189  	}
   190  }
   191  
   192  // WroteHeaderField is called after the Transport has written
   193  // each request header. At the time of this call the values
   194  // might be buffered and not yet written to the network.
   195  func (ct *clientTracerTracing) WroteHeaderField(k string, v []string) {
   196  	if len(v) > 1 {
   197  		ct.headers[k] = v
   198  	} else if len(v) == 1 {
   199  		ct.headers[k] = v[0]
   200  	}
   201  }
   202  
   203  // WroteHeaders is called after the Transport has written
   204  // all request headers.
   205  func (ct *clientTracerTracing) WroteHeaders() {}
   206  
   207  // Wait100Continue is called if the Request specified
   208  // "Expect: 100-continue" and the Transport has written the
   209  // request headers but is waiting for "100 Continue" from the
   210  // server before writing the request body.
   211  func (ct *clientTracerTracing) Wait100Continue() {}
   212  
   213  // WroteRequest is called with the result of writing the
   214  // request and any body. It may be called multiple times
   215  // in the case of retried requests.
   216  func (ct *clientTracerTracing) WroteRequest(info httptrace.WroteRequestInfo) {
   217  	if info.Err != nil {
   218  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
   219  	}
   220  
   221  	ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
   222  		attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)),
   223  		attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()),
   224  	))
   225  }