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:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user