gf/net/gclient/gclient_tracing_tracer.go

177 lines
4.4 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gclient
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptrace"
"net/textproto"
"strings"
"sync"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
type clientTracer struct {
context.Context
span trace.Span
request *http.Request
requestBody []byte
headers map[string]interface{}
mtx sync.Mutex
}
func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) *httptrace.ClientTrace {
ct := &clientTracer{
Context: ctx,
span: span,
request: request,
headers: make(map[string]interface{}),
}
reqBodyContent, _ := ioutil.ReadAll(ct.request.Body)
ct.requestBody = reqBodyContent
ct.request.Body = utils.NewReadCloser(reqBodyContent, false)
return &httptrace.ClientTrace{
GetConn: ct.getConn,
GotConn: ct.gotConn,
PutIdleConn: ct.putIdleConn,
GotFirstResponseByte: ct.gotFirstResponseByte,
Got100Continue: ct.got100Continue,
Got1xxResponse: ct.got1xxResponse,
DNSStart: ct.dnsStart,
DNSDone: ct.dnsDone,
ConnectStart: ct.connectStart,
ConnectDone: ct.connectDone,
TLSHandshakeStart: ct.tlsHandshakeStart,
TLSHandshakeDone: ct.tlsHandshakeDone,
WroteHeaderField: ct.wroteHeaderField,
WroteHeaders: ct.wroteHeaders,
Wait100Continue: ct.wait100Continue,
WroteRequest: ct.wroteRequest,
}
}
func (ct *clientTracer) getConn(host string) {
}
func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) {
ct.span.SetAttributes(
attribute.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()),
attribute.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()),
)
}
func (ct *clientTracer) putIdleConn(err error) {
if err != nil {
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
}
}
func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
ct.span.SetAttributes(
attribute.String(tracingAttrHttpDnsStart, info.Host),
)
}
func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
var buffer strings.Builder
for _, v := range info.Addrs {
if buffer.Len() != 0 {
buffer.WriteString(",")
}
buffer.WriteString(v.String())
}
if info.Err != nil {
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
}
ct.span.SetAttributes(
attribute.String(tracingAttrHttpDnsDone, buffer.String()),
)
}
func (ct *clientTracer) connectStart(network, addr string) {
ct.span.SetAttributes(
attribute.String(tracingAttrHttpConnectStart, network+"@"+addr),
)
}
func (ct *clientTracer) connectDone(network, addr string, err error) {
if err != nil {
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
}
ct.span.SetAttributes(
attribute.String(tracingAttrHttpConnectDone, network+"@"+addr),
)
}
func (ct *clientTracer) tlsHandshakeStart() {
}
func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) {
if err != nil {
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
}
}
func (ct *clientTracer) wroteHeaderField(k string, v []string) {
if len(v) > 1 {
ct.headers[k] = v
} else {
ct.headers[k] = v[0]
}
}
func (ct *clientTracer) wroteHeaders() {
}
func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) {
if info.Err != nil {
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
}
ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)),
attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()),
attribute.String(tracingEventHttpRequestBody, gstr.StrLimit(
string(ct.requestBody),
gtrace.MaxContentLogSize(),
"...",
)),
))
}
func (ct *clientTracer) got100Continue() {
}
func (ct *clientTracer) wait100Continue() {
}
func (ct *clientTracer) gotFirstResponseByte() {
}
func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error {
return nil
}