feat: opentelemetry integration, removed self designed tracing (#1111)

* feat: opentelemetry integration, removed self designed tracing

* feat: support zipkin on opentelemetry integration

* feat: support zipkin on opentelemetry integration, enable it in conf

* style: format code

* fix: support logx without exporter configured

* fix: check return values

* refactor: simplify code

* refactor: simplify opentelemetry integration

* ci: fix staticcheck errors
This commit is contained in:
Kevin Wan
2021-10-03 20:53:50 +08:00
committed by GitHub
parent 6e34b55ba7
commit 10e7922597
53 changed files with 611 additions and 1525 deletions

View File

@@ -3,50 +3,116 @@ package serverinterceptors
import (
"context"
"github.com/tal-tech/go-zero/core/trace"
ztrace "github.com/tal-tech/go-zero/core/trace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
gcodes "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// UnaryTracingInterceptor returns a grpc.UnaryServerInterceptor
// that handles tracing with given service name.
func UnaryTracingInterceptor(serviceName string) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (resp interface{}, err error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return handler(ctx, req)
}
// UnaryTracingInterceptor is a grpc.UnaryServerInterceptor for opentelemetry.
func UnaryTracingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
ctx, span := startSpan(ctx, info.FullMethod)
defer span.End()
carrier, err := trace.Extract(trace.GrpcFormat, md)
if err != nil {
return handler(ctx, req)
ztrace.MessageReceived.Event(ctx, 1, req)
resp, err := handler(ctx, req)
if err != nil {
s, ok := status.FromError(err)
if ok {
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))
ztrace.MessageSent.Event(ctx, 1, s.Proto())
} else {
span.SetStatus(codes.Error, err.Error())
}
ctx, span := trace.StartServerSpan(ctx, carrier, serviceName, info.FullMethod)
defer span.Finish()
return handler(ctx, req)
return nil, err
}
span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))
ztrace.MessageSent.Event(ctx, 1, resp)
return resp, nil
}
// StreamTracingInterceptor returns a grpc.StreamServerInterceptor
// that handles tracing with given service name.
func StreamTracingInterceptor(serviceName string) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo,
handler grpc.StreamHandler) error {
ctx := ss.Context()
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return handler(srv, ss)
}
// StreamTracingInterceptor returns a grpc.StreamServerInterceptor for opentelemetry.
func StreamTracingInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo,
handler grpc.StreamHandler) error {
ctx, span := startSpan(ss.Context(), info.FullMethod)
defer span.End()
carrier, err := trace.Extract(trace.GrpcFormat, md)
if err != nil {
return handler(srv, ss)
if err := handler(srv, wrapServerStream(ctx, ss)); err != nil {
s, ok := status.FromError(err)
if ok {
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))
} else {
span.SetStatus(codes.Error, err.Error())
}
return err
}
_, span := trace.StartServerSpan(ctx, carrier, serviceName, info.FullMethod)
defer span.Finish()
return handler(srv, ss)
span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))
return nil
}
// serverStream wraps around the embedded grpc.ServerStream,
// and intercepts the RecvMsg and SendMsg method call.
type serverStream struct {
grpc.ServerStream
ctx context.Context
receivedMessageID int
sentMessageID int
}
func (w *serverStream) Context() context.Context {
return w.ctx
}
func (w *serverStream) RecvMsg(m interface{}) error {
err := w.ServerStream.RecvMsg(m)
if err == nil {
w.receivedMessageID++
ztrace.MessageReceived.Event(w.Context(), w.receivedMessageID, m)
}
return err
}
func (w *serverStream) SendMsg(m interface{}) error {
err := w.ServerStream.SendMsg(m)
w.sentMessageID++
ztrace.MessageSent.Event(w.Context(), w.sentMessageID, m)
return err
}
func startSpan(ctx context.Context, method string) (context.Context, trace.Span) {
var md metadata.MD
requestMetadata, ok := metadata.FromIncomingContext(ctx)
if ok {
md = requestMetadata.Copy()
} else {
md = metadata.MD{}
}
bags, spanCtx := ztrace.Extract(ctx, otel.GetTextMapPropagator(), &md)
ctx = baggage.ContextWithBaggage(ctx, bags)
tr := otel.Tracer(ztrace.TraceName)
name, attr := ztrace.SpanInfo(method, ztrace.PeerFromCtx(ctx))
return tr.Start(trace.ContextWithRemoteSpanContext(ctx, spanCtx), name,
trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(attr...))
}
// wrapServerStream wraps the given grpc.ServerStream with the given context.
func wrapServerStream(ctx context.Context, ss grpc.ServerStream) *serverStream {
return &serverStream{
ServerStream: ss,
ctx: ctx,
}
}