github.com/songzhibin97/gkit@v1.2.13/trace/span.go (about)

     1  package trace
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/url"
     7  	"strings"
     8  
     9  	"github.com/songzhibin97/gkit/internal/metadata"
    10  	"go.opentelemetry.io/otel/attribute"
    11  	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    12  	"go.opentelemetry.io/otel/trace"
    13  	"google.golang.org/grpc/peer"
    14  	"google.golang.org/protobuf/proto"
    15  )
    16  
    17  func setClientSpan(ctx context.Context, span trace.Span, m interface{}) {
    18  	var attrs []attribute.KeyValue
    19  	var remote string
    20  	var operation string
    21  	var rpcKind string
    22  	if tr, ok := FromClientTransportContext(ctx); ok {
    23  		operation = tr.Operation()
    24  		rpcKind = tr.Kind().String()
    25  		if tr.Kind() == KindHTTP {
    26  			if ht, ok := tr.(*Transport); ok {
    27  				method := ht.Request().Method
    28  				route := ht.PathTemplate()
    29  				path := ht.Request().URL.Path
    30  				attrs = append(attrs, semconv.HTTPMethodKey.String(method))
    31  				attrs = append(attrs, semconv.HTTPRouteKey.String(route))
    32  				attrs = append(attrs, semconv.HTTPTargetKey.String(path))
    33  				remote = ht.Request().Host
    34  			}
    35  		} else if tr.Kind() == KindGRPC {
    36  			remote, _ = parseTarget(tr.Endpoint())
    37  		}
    38  	}
    39  	attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
    40  	_, mAttrs := parseFullMethod(operation)
    41  	attrs = append(attrs, mAttrs...)
    42  	if remote != "" {
    43  		attrs = append(attrs, peerAttr(remote)...)
    44  	}
    45  	if p, ok := m.(proto.Message); ok {
    46  		attrs = append(attrs, attribute.Key("send_msg.size").Int(proto.Size(p)))
    47  	}
    48  
    49  	span.SetAttributes(attrs...)
    50  }
    51  
    52  func setServerSpan(ctx context.Context, span trace.Span, m interface{}) {
    53  	attrs := []attribute.KeyValue{}
    54  	var remote string
    55  	var operation string
    56  	var rpcKind string
    57  	if tr, ok := FromServerTransportContext(ctx); ok {
    58  		operation = tr.Operation()
    59  		rpcKind = tr.Kind().String()
    60  		if tr.Kind() == KindHTTP {
    61  			if ht, ok := tr.(*Transport); ok {
    62  				method := ht.Request().Method
    63  				route := ht.PathTemplate()
    64  				path := ht.Request().URL.Path
    65  				attrs = append(attrs, semconv.HTTPMethodKey.String(method))
    66  				attrs = append(attrs, semconv.HTTPRouteKey.String(route))
    67  				attrs = append(attrs, semconv.HTTPTargetKey.String(path))
    68  				remote = ht.Request().RemoteAddr
    69  			}
    70  		} else if tr.Kind() == KindGRPC {
    71  			if p, ok := peer.FromContext(ctx); ok {
    72  				remote = p.Addr.String()
    73  			}
    74  		}
    75  	}
    76  	attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
    77  	_, mAttrs := parseFullMethod(operation)
    78  	attrs = append(attrs, mAttrs...)
    79  	attrs = append(attrs, peerAttr(remote)...)
    80  	if p, ok := m.(proto.Message); ok {
    81  		attrs = append(attrs, attribute.Key("receive_msg.size").Int(proto.Size(p)))
    82  	}
    83  	if md, ok := metadata.FromServerContext(ctx); ok {
    84  		attrs = append(attrs, semconv.PeerServiceKey.String(md.GetValue(serverMark)))
    85  	}
    86  
    87  	span.SetAttributes(attrs...)
    88  }
    89  
    90  // parseFullMethod returns a span name following the OpenTelemetry semantic
    91  // conventions as well as all applicable span attribute.KeyValue attributes based
    92  // on a gRPC's FullMethod.
    93  func parseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
    94  	name := strings.TrimLeft(fullMethod, "/")
    95  	parts := strings.SplitN(name, "/", 2)
    96  	if len(parts) != 2 {
    97  		// Invalid format, does not follow `/package.service/method`.
    98  		return name, []attribute.KeyValue{attribute.Key("rpc.operation").String(fullMethod)}
    99  	}
   100  
   101  	var attrs []attribute.KeyValue
   102  	if service := parts[0]; service != "" {
   103  		attrs = append(attrs, semconv.RPCServiceKey.String(service))
   104  	}
   105  	if method := parts[1]; method != "" {
   106  		attrs = append(attrs, semconv.RPCMethodKey.String(method))
   107  	}
   108  	return name, attrs
   109  }
   110  
   111  // peerAttr returns attributes about the peer address.
   112  func peerAttr(addr string) []attribute.KeyValue {
   113  	host, port, err := net.SplitHostPort(addr)
   114  	if err != nil {
   115  		return []attribute.KeyValue(nil)
   116  	}
   117  
   118  	if host == "" {
   119  		host = "127.0.0.1"
   120  	}
   121  
   122  	return []attribute.KeyValue{
   123  		semconv.NetPeerIPKey.String(host),
   124  		semconv.NetPeerPortKey.String(port),
   125  	}
   126  }
   127  
   128  func parseTarget(endpoint string) (address string, err error) {
   129  	var u *url.URL
   130  	u, err = url.Parse(endpoint)
   131  	if err != nil {
   132  		if u, err = url.Parse("http://" + endpoint); err != nil {
   133  			return "", err
   134  		}
   135  		return u.Host, nil
   136  	}
   137  	if len(u.Path) > 1 {
   138  		return u.Path[1:], nil
   139  	}
   140  	return endpoint, nil
   141  }