| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 | package grpcsentryimport (	"context"	"fmt"	"kpt-grpc-demo/util/xerr"	"github.com/getsentry/sentry-go"	grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware"	ctxTag "github.com/grpc-ecosystem/go-grpc-middleware/tags"	"google.golang.org/grpc"	"google.golang.org/grpc/codes"	"google.golang.org/grpc/status")var (	defaultOptions = &options{		reportDecider: defaultReportDecider,		rePanic:       false,	})type options struct {	reportDecider func(err error) bool	rePanic       bool}func evaluateServerOpt(opts []Option) *options {	optCopy := &options{}	*optCopy = *defaultOptions	for _, o := range opts {		o(optCopy)	}	return optCopy}type Option func(*options)// WithCodes customizes the function for mapping errors to error codes.func WithReportDecider(f func(err error) bool) Option {	return func(o *options) {		o.reportDecider = f	}}func WithRePanic(v bool) Option {	return func(o *options) {		o.rePanic = v	}}func defaultReportDecider(err error) bool {	_, isCustom := xerr.IsCustomError(err)	if isCustom {		return false	}	code := status.Code(err)	switch code {	case codes.Unknown, codes.Unimplemented, codes.Internal, codes.DeadlineExceeded, codes.DataLoss:		return true	default:		return false	}}// WithUnaryServerHandler intercept unary grpc handler and report error to sentryfunc WithUnaryServerHandler(opts ...Option) grpc.UnaryServerInterceptor {	o := evaluateServerOpt(opts)	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {		var hub *sentry.Hub		hub, ctx = newHubForCall(ctx, info.FullMethod)		hub.Scope().SetExtra("request", req)		// Recover and capture panic		defer func(ctx context.Context) {			if rval := recover(); rval != nil {				capturePanicWithContext(ctx, rval)				if o.rePanic {					panic(rval)				}				err = status.Error(codes.Internal, fmt.Sprint(rval))			}		}(ctx)		resp, err = handler(ctx, req)		if o.reportDecider(err) {			reportError(ctx, err)		}		return resp, err	}}// WithStreamServerHandler intercept stream grpc handler and report error to sentryfunc WithStreamServerHandler(opts ...Option) grpc.StreamServerInterceptor {	o := evaluateServerOpt(opts)	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {		_, newCtx := newHubForCall(stream.Context(), info.FullMethod)		wrappedStream := grpcMiddleware.WrapServerStream(stream)		wrappedStream.WrappedContext = newCtx		stream = wrappedStream		// Recover and capture panic		defer func(ctx context.Context) {			if rval := recover(); rval != nil {				capturePanicWithContext(ctx, rval)				if o.rePanic {					panic(rval)				}				err = status.Error(codes.Internal, fmt.Sprint(rval))			}		}(stream.Context())		err = handler(srv, stream)		if o.reportDecider(err) {			reportError(stream.Context(), err)		}		return err	}}// report if err != nilfunc reportError(ctx context.Context, err error) {	errCode := status.Code(err)	hub := sentry.GetHubFromContext(ctx)	if hub == nil {		return	}	sentryExtras := ctxTag.Extract(ctx).Values()	sentryTags := make(map[string]string)	sentryTags["grpc.code"] = errCode.String()	hub.ConfigureScope(func(scope *sentry.Scope) {		scope.SetTags(sentryTags)		scope.SetExtras(sentryExtras)	})	hub.CaptureException(err)}func capturePanicWithContext(ctx context.Context, err interface{}) {	hub := sentry.GetHubFromContext(ctx)	if hub == nil {		return	}	hub.ConfigureScope(func(scope *sentry.Scope) {		scope.SetExtras(ctxTag.Extract(ctx).Values())	})	_ = hub.RecoverWithContext(ctx, err)}func newHubForCall(ctx context.Context, fullMethodString string) (*sentry.Hub, context.Context) {	hub := sentry.CurrentHub().Clone()	hub.ConfigureScope(func(scope *sentry.Scope) {		scope.SetTag("grpc_method", fullMethodString)	})	newCtx := sentry.SetHubOnContext(ctx, hub)	return hub, newCtx}
 |