Skip to content

Commit cdc9dde

Browse files
committed
feat: add client tracing interceptor
1 parent 86d5fa8 commit cdc9dde

File tree

7 files changed

+434
-1
lines changed

7 files changed

+434
-1
lines changed

gmicro/core/trace/attributes.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package trace
2+
3+
import (
4+
"go.opentelemetry.io/otel/attribute"
5+
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
6+
gcodes "google.golang.org/grpc/codes"
7+
)
8+
9+
const (
10+
// GRPCStatusCodeKey is convention for numeric status code of a gRPC request.
11+
GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code")
12+
// RPCNameKey is the name of message transmitted or received.
13+
RPCNameKey = attribute.Key("name")
14+
// RPCMessageTypeKey is the type of message transmitted or received.
15+
RPCMessageTypeKey = attribute.Key("message.type")
16+
// RPCMessageIDKey is the identifier of message transmitted or received.
17+
RPCMessageIDKey = attribute.Key("message.id")
18+
// RPCMessageCompressedSizeKey is the compressed size of the message transmitted or received in bytes.
19+
RPCMessageCompressedSizeKey = attribute.Key("message.compressed_size")
20+
// RPCMessageUncompressedSizeKey is the uncompressed size of the message
21+
// transmitted or received in bytes.
22+
RPCMessageUncompressedSizeKey = attribute.Key("message.uncompressed_size")
23+
)
24+
25+
// Semantic conventions for common RPC attributes.
26+
var (
27+
// RPCSystemGRPC is the semantic convention for gRPC as the remoting system.
28+
RPCSystemGRPC = semconv.RPCSystemKey.String("grpc")
29+
// RPCNameMessage is the semantic convention for a message named message.
30+
RPCNameMessage = RPCNameKey.String("message")
31+
// RPCMessageTypeSent is the semantic conventions for sent RPC message types.
32+
RPCMessageTypeSent = RPCMessageTypeKey.String("SENT")
33+
// RPCMessageTypeReceived is the semantic conventions for the received RPC message types.
34+
RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED")
35+
)
36+
37+
// StatusCodeAttr returns an attribute.KeyValue that represents the give c.
38+
func StatusCodeAttr(c gcodes.Code) attribute.KeyValue {
39+
return GRPCStatusCodeKey.Int64(int64(c))
40+
}

gmicro/core/trace/message.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package trace
2+
3+
import (
4+
"context"
5+
6+
"go.opentelemetry.io/otel/attribute"
7+
"go.opentelemetry.io/otel/trace"
8+
"google.golang.org/protobuf/proto"
9+
)
10+
11+
const messageEvent = "message"
12+
13+
var (
14+
// MessageSent is the type of sent messages.
15+
MessageSent = messageType(RPCMessageTypeSent)
16+
// MessageReceived is the type of received messages.
17+
MessageReceived = messageType(RPCMessageTypeReceived)
18+
)
19+
20+
type messageType attribute.KeyValue
21+
22+
// Event adds an event of the messageType to the span associated with the
23+
// passed context with id and size (if message is a proto message).
24+
func (m messageType) Event(ctx context.Context, id int, message interface{}) {
25+
span := trace.SpanFromContext(ctx)
26+
if p, ok := message.(proto.Message); ok {
27+
span.AddEvent(messageEvent, trace.WithAttributes(
28+
attribute.KeyValue(m),
29+
RPCMessageIDKey.Int(id),
30+
RPCMessageUncompressedSizeKey.Int(proto.Size(p)),
31+
))
32+
} else {
33+
span.AddEvent(messageEvent, trace.WithAttributes(
34+
attribute.KeyValue(m),
35+
RPCMessageIDKey.Int(id),
36+
))
37+
}
38+
}

gmicro/core/trace/options.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package trace
22

3+
//const TraceName = "gmicro"
4+
35
// Options 这个配置和 业务中 app 中的 tracing 冗余,为的是将 tracing 从业务中解耦出来
46
type Options struct {
57
Name string `json:"name"` // jeager 名称

gmicro/core/trace/tracer.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package trace
2+
3+
import (
4+
"context"
5+
6+
"go.opentelemetry.io/otel/baggage"
7+
"go.opentelemetry.io/otel/propagation"
8+
sdktrace "go.opentelemetry.io/otel/trace"
9+
"google.golang.org/grpc/metadata"
10+
)
11+
12+
// assert that metadataSupplier implements the TextMapCarrier interface
13+
var _ propagation.TextMapCarrier = (*metadataSupplier)(nil)
14+
15+
type metadataSupplier struct {
16+
metadata *metadata.MD
17+
}
18+
19+
func (s *metadataSupplier) Get(key string) string {
20+
values := s.metadata.Get(key)
21+
if len(values) == 0 {
22+
return ""
23+
}
24+
25+
return values[0]
26+
}
27+
28+
func (s *metadataSupplier) Set(key, value string) {
29+
s.metadata.Set(key, value)
30+
}
31+
32+
func (s *metadataSupplier) Keys() []string {
33+
out := make([]string, 0, len(*s.metadata))
34+
for key := range *s.metadata {
35+
out = append(out, key)
36+
}
37+
38+
return out
39+
}
40+
41+
// Inject injects the metadata into ctx.
42+
func Inject(ctx context.Context, p propagation.TextMapPropagator, metadata *metadata.MD) {
43+
p.Inject(ctx, &metadataSupplier{
44+
metadata: metadata,
45+
})
46+
}
47+
48+
// Extract extracts the metadata from ctx.
49+
func Extract(ctx context.Context, p propagation.TextMapPropagator, metadata *metadata.MD) (
50+
baggage.Baggage, sdktrace.SpanContext) {
51+
ctx = p.Extract(ctx, &metadataSupplier{
52+
metadata: metadata,
53+
})
54+
55+
return baggage.FromContext(ctx), sdktrace.SpanContextFromContext(ctx)
56+
}

gmicro/core/trace/utils.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package trace
2+
3+
import (
4+
"context"
5+
"net"
6+
"strings"
7+
8+
"go.opentelemetry.io/otel/attribute"
9+
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
10+
"google.golang.org/grpc/peer"
11+
)
12+
13+
const localhost = "127.0.0.1"
14+
15+
// PeerFromCtx returns the peer from ctx.
16+
func PeerFromCtx(ctx context.Context) string {
17+
p, ok := peer.FromContext(ctx)
18+
if !ok || p == nil {
19+
return ""
20+
}
21+
22+
return p.Addr.String()
23+
}
24+
25+
// SpanInfo returns the span info.
26+
func SpanInfo(fullMethod, peerAddress string) (string, []attribute.KeyValue) {
27+
attrs := []attribute.KeyValue{RPCSystemGRPC}
28+
name, mAttrs := ParseFullMethod(fullMethod)
29+
attrs = append(attrs, mAttrs...)
30+
attrs = append(attrs, PeerAttr(peerAddress)...)
31+
return name, attrs
32+
}
33+
34+
// ParseFullMethod returns the method name and attributes.
35+
func ParseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
36+
name := strings.TrimLeft(fullMethod, "/")
37+
parts := strings.SplitN(name, "/", 2)
38+
if len(parts) != 2 {
39+
// Invalid format, does not follow `/package.service/method`.
40+
return name, []attribute.KeyValue(nil)
41+
}
42+
43+
var attrs []attribute.KeyValue
44+
if service := parts[0]; service != "" {
45+
attrs = append(attrs, semconv.RPCServiceKey.String(service))
46+
}
47+
if method := parts[1]; method != "" {
48+
attrs = append(attrs, semconv.RPCMethodKey.String(method))
49+
}
50+
51+
return name, attrs
52+
}
53+
54+
// PeerAttr returns the peer attributes.
55+
func PeerAttr(addr string) []attribute.KeyValue {
56+
host, port, err := net.SplitHostPort(addr)
57+
if err != nil {
58+
return nil
59+
}
60+
61+
if len(host) == 0 {
62+
host = localhost
63+
}
64+
65+
return []attribute.KeyValue{
66+
semconv.NetPeerIPKey.String(host),
67+
semconv.NetPeerPortKey.String(port),
68+
}
69+
}

gmicro/server/restserver/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func NewServer(opts ...ServerOption) *Server {
6363
jwt: &JwtInfo{
6464
"JWT",
6565
"Gd%YCfP1agNHo5x6xm2Qs33Bf!B#Gi!o",
66-
7 * 24 * time.Hour,
66+
1 * 24 * time.Hour,
6767
7 * 24 * time.Hour,
6868
},
6969
Engine: gin.Default(),

0 commit comments

Comments
 (0)