github.com/wangyougui/gf/v2@v2.6.5/net/gclient/gclient_tracing_tracer.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  	"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/wangyougui/gf/v2/internal/utils"
    25  	"github.com/wangyougui/gf/v2/net/gtrace"
    26  	"github.com/wangyougui/gf/v2/util/gconv"
    27  )
    28  
    29  // clientTracer is used for implementing httptrace.ClientTrace.
    30  type clientTracer 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  // newClientTrace creates and returns object of newClientTrace.
    40  func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) *httptrace.ClientTrace {
    41  	ct := &clientTracer{
    42  		Context: ctx,
    43  		span:    span,
    44  		request: request,
    45  		headers: make(map[string]interface{}),
    46  	}
    47  
    48  	reqBodyContent, _ := io.ReadAll(ct.request.Body)
    49  	ct.requestBody = reqBodyContent
    50  	ct.request.Body = utils.NewReadCloser(reqBodyContent, false)
    51  
    52  	return &httptrace.ClientTrace{
    53  		GetConn:              ct.getConn,
    54  		GotConn:              ct.gotConn,
    55  		PutIdleConn:          ct.putIdleConn,
    56  		GotFirstResponseByte: ct.gotFirstResponseByte,
    57  		Got100Continue:       ct.got100Continue,
    58  		Got1xxResponse:       ct.got1xxResponse,
    59  		DNSStart:             ct.dnsStart,
    60  		DNSDone:              ct.dnsDone,
    61  		ConnectStart:         ct.connectStart,
    62  		ConnectDone:          ct.connectDone,
    63  		TLSHandshakeStart:    ct.tlsHandshakeStart,
    64  		TLSHandshakeDone:     ct.tlsHandshakeDone,
    65  		WroteHeaderField:     ct.wroteHeaderField,
    66  		WroteHeaders:         ct.wroteHeaders,
    67  		Wait100Continue:      ct.wait100Continue,
    68  		WroteRequest:         ct.wroteRequest,
    69  	}
    70  }
    71  
    72  func (ct *clientTracer) getConn(host string) {}
    73  
    74  func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) {
    75  	remoteAddr := ""
    76  	if info.Conn.RemoteAddr() != nil {
    77  		remoteAddr = info.Conn.RemoteAddr().String()
    78  	}
    79  	localAddr := ""
    80  	if info.Conn.LocalAddr() != nil {
    81  		localAddr = info.Conn.LocalAddr().String()
    82  	}
    83  	ct.span.SetAttributes(
    84  		attribute.String(tracingAttrHttpAddressRemote, remoteAddr),
    85  		attribute.String(tracingAttrHttpAddressLocal, localAddr),
    86  	)
    87  }
    88  
    89  func (ct *clientTracer) putIdleConn(err error) {
    90  	if err != nil {
    91  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
    92  	}
    93  }
    94  
    95  func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
    96  	ct.span.SetAttributes(
    97  		attribute.String(tracingAttrHttpDnsStart, info.Host),
    98  	)
    99  }
   100  
   101  func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
   102  	var buffer strings.Builder
   103  	for _, v := range info.Addrs {
   104  		if buffer.Len() != 0 {
   105  			buffer.WriteString(",")
   106  		}
   107  		buffer.WriteString(v.String())
   108  	}
   109  	if info.Err != nil {
   110  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
   111  	}
   112  	ct.span.SetAttributes(
   113  		attribute.String(tracingAttrHttpDnsDone, buffer.String()),
   114  	)
   115  }
   116  
   117  func (ct *clientTracer) connectStart(network, addr string) {
   118  	ct.span.SetAttributes(
   119  		attribute.String(tracingAttrHttpConnectStart, network+"@"+addr),
   120  	)
   121  }
   122  
   123  func (ct *clientTracer) connectDone(network, addr string, err error) {
   124  	if err != nil {
   125  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   126  	}
   127  	ct.span.SetAttributes(
   128  		attribute.String(tracingAttrHttpConnectDone, network+"@"+addr),
   129  	)
   130  }
   131  
   132  func (ct *clientTracer) tlsHandshakeStart() {}
   133  
   134  func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) {
   135  	if err != nil {
   136  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
   137  	}
   138  }
   139  
   140  func (ct *clientTracer) wroteHeaderField(k string, v []string) {
   141  	if len(v) > 1 {
   142  		ct.headers[k] = v
   143  	} else if len(v) == 1 {
   144  		ct.headers[k] = v[0]
   145  	}
   146  }
   147  
   148  func (ct *clientTracer) wroteHeaders() {}
   149  
   150  func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) {
   151  	if info.Err != nil {
   152  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
   153  	}
   154  
   155  	reqBodyContent, err := gtrace.SafeContentForHttp(ct.requestBody, ct.request.Header)
   156  	if err != nil {
   157  		ct.span.SetStatus(codes.Error, fmt.Sprintf(`converting safe content failed: %s`, err.Error()))
   158  	}
   159  
   160  	ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
   161  		attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)),
   162  		attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()),
   163  		attribute.String(tracingEventHttpRequestBody, reqBodyContent),
   164  	))
   165  }
   166  
   167  func (ct *clientTracer) got100Continue() {}
   168  
   169  func (ct *clientTracer) wait100Continue() {}
   170  
   171  func (ct *clientTracer) gotFirstResponseByte() {}
   172  
   173  func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error {
   174  	return nil
   175  }