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 }