You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.2 KiB
149 lines
4.2 KiB
2 years ago
|
package tracing
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"net"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/go-kratos/kratos/v2/metadata"
|
||
|
"github.com/go-kratos/kratos/v2/transport"
|
||
|
"github.com/go-kratos/kratos/v2/transport/http"
|
||
|
|
||
|
"go.opentelemetry.io/otel/attribute"
|
||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||
|
"go.opentelemetry.io/otel/trace"
|
||
|
"google.golang.org/grpc/peer"
|
||
|
"google.golang.org/protobuf/proto"
|
||
|
)
|
||
|
|
||
|
func setClientSpan(ctx context.Context, span trace.Span, m interface{}) {
|
||
|
attrs := []attribute.KeyValue{}
|
||
|
var remote string
|
||
|
var operation string
|
||
|
var rpcKind string
|
||
|
tr, ok := transport.FromClientContext(ctx)
|
||
|
if ok {
|
||
|
operation = tr.Operation()
|
||
|
rpcKind = tr.Kind().String()
|
||
|
switch tr.Kind() {
|
||
|
case transport.KindHTTP:
|
||
|
if ht, ok := tr.(http.Transporter); ok {
|
||
|
method := ht.Request().Method
|
||
|
route := ht.PathTemplate()
|
||
|
path := ht.Request().URL.Path
|
||
|
attrs = append(attrs, semconv.HTTPMethodKey.String(method))
|
||
|
attrs = append(attrs, semconv.HTTPRouteKey.String(route))
|
||
|
attrs = append(attrs, semconv.HTTPTargetKey.String(path))
|
||
|
remote = ht.Request().Host
|
||
|
}
|
||
|
case transport.KindGRPC:
|
||
|
remote, _ = parseTarget(tr.Endpoint())
|
||
|
}
|
||
|
}
|
||
|
attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
|
||
|
_, mAttrs := parseFullMethod(operation)
|
||
|
attrs = append(attrs, mAttrs...)
|
||
|
if remote != "" {
|
||
|
attrs = append(attrs, peerAttr(remote)...)
|
||
|
}
|
||
|
if p, ok := m.(proto.Message); ok {
|
||
|
attrs = append(attrs, attribute.Key("send_msg.size").Int(proto.Size(p)))
|
||
|
}
|
||
|
|
||
|
span.SetAttributes(attrs...)
|
||
|
}
|
||
|
|
||
|
func setServerSpan(ctx context.Context, span trace.Span, m interface{}) {
|
||
|
attrs := []attribute.KeyValue{}
|
||
|
var remote string
|
||
|
var operation string
|
||
|
var rpcKind string
|
||
|
tr, ok := transport.FromServerContext(ctx)
|
||
|
if ok {
|
||
|
operation = tr.Operation()
|
||
|
rpcKind = tr.Kind().String()
|
||
|
switch tr.Kind() {
|
||
|
case transport.KindHTTP:
|
||
|
if ht, ok := tr.(http.Transporter); ok {
|
||
|
method := ht.Request().Method
|
||
|
route := ht.PathTemplate()
|
||
|
path := ht.Request().URL.Path
|
||
|
attrs = append(attrs, semconv.HTTPMethodKey.String(method))
|
||
|
attrs = append(attrs, semconv.HTTPRouteKey.String(route))
|
||
|
attrs = append(attrs, semconv.HTTPTargetKey.String(path))
|
||
|
remote = ht.Request().RemoteAddr
|
||
|
}
|
||
|
case transport.KindGRPC:
|
||
|
if p, ok := peer.FromContext(ctx); ok {
|
||
|
remote = p.Addr.String()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
attrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))
|
||
|
_, mAttrs := parseFullMethod(operation)
|
||
|
attrs = append(attrs, mAttrs...)
|
||
|
attrs = append(attrs, peerAttr(remote)...)
|
||
|
if p, ok := m.(proto.Message); ok {
|
||
|
attrs = append(attrs, attribute.Key("recv_msg.size").Int(proto.Size(p)))
|
||
|
}
|
||
|
if md, ok := metadata.FromServerContext(ctx); ok {
|
||
|
attrs = append(attrs, semconv.PeerServiceKey.String(md.Get(serviceHeader)))
|
||
|
}
|
||
|
|
||
|
span.SetAttributes(attrs...)
|
||
|
}
|
||
|
|
||
|
// parseFullMethod returns a span name following the OpenTelemetry semantic
|
||
|
// conventions as well as all applicable span attribute.KeyValue attributes based
|
||
|
// on a gRPC's FullMethod.
|
||
|
func parseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
|
||
|
name := strings.TrimLeft(fullMethod, "/")
|
||
|
parts := strings.SplitN(name, "/", 2)
|
||
|
if len(parts) != 2 { //nolint:gomnd
|
||
|
// Invalid format, does not follow `/package.service/method`.
|
||
|
return name, []attribute.KeyValue{attribute.Key("rpc.operation").String(fullMethod)}
|
||
|
}
|
||
|
|
||
|
var attrs []attribute.KeyValue
|
||
|
if service := parts[0]; service != "" {
|
||
|
attrs = append(attrs, semconv.RPCServiceKey.String(service))
|
||
|
}
|
||
|
if method := parts[1]; method != "" {
|
||
|
attrs = append(attrs, semconv.RPCMethodKey.String(method))
|
||
|
}
|
||
|
return name, attrs
|
||
|
}
|
||
|
|
||
|
// peerAttr returns attributes about the peer address.
|
||
|
func peerAttr(addr string) []attribute.KeyValue {
|
||
|
host, port, err := net.SplitHostPort(addr)
|
||
|
if err != nil {
|
||
|
return []attribute.KeyValue(nil)
|
||
|
}
|
||
|
|
||
|
if host == "" {
|
||
|
host = "127.0.0.1"
|
||
|
}
|
||
|
|
||
|
return []attribute.KeyValue{
|
||
|
semconv.NetPeerIPKey.String(host),
|
||
|
semconv.NetPeerPortKey.String(port),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseTarget(endpoint string) (address string, err error) {
|
||
|
var u *url.URL
|
||
|
u, err = url.Parse(endpoint)
|
||
|
if err != nil {
|
||
|
if u, err = url.Parse("http://" + endpoint); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return u.Host, nil
|
||
|
}
|
||
|
if len(u.Path) > 1 {
|
||
|
return u.Path[1:], nil
|
||
|
}
|
||
|
return endpoint, nil
|
||
|
}
|