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 }