gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/go-grpc-middleware/logging/logrus/payload_interceptors.go (about)

     1  package grpc_logrus
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"gitee.com/zhaochuninhefei/gmgo/go-grpc-middleware/logging/logrus/ctxlogrus"
     7  
     8  	grpclogging "gitee.com/zhaochuninhefei/gmgo/go-grpc-middleware/logging"
     9  	"gitee.com/zhaochuninhefei/gmgo/grpc"
    10  	"gitee.com/zhaochuninhefei/gmgo/net/context"
    11  	"github.com/golang/protobuf/jsonpb"
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  var (
    17  	// JsonPbMarshaller is the marshaller used for serializing protobuf messages.
    18  	JsonPbMarshaller = &jsonpb.Marshaler{}
    19  )
    20  
    21  // PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
    22  //
    23  // This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a
    24  // separate instance of the logger.
    25  func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpclogging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor {
    26  	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    27  		if !decider(ctx, info.FullMethod, info.Server) {
    28  			return handler(ctx, req)
    29  		}
    30  		// Use the provided logrus.Entry for logging but use the fields from context.
    31  		// ctx_logrus.Extract is deprecated, use the ctxlogrus.Extract instead.
    32  		//logEntry := entry.WithFields(ctx_logrus.Extract(ctx).Data)
    33  		logEntry := entry.WithFields(ctxlogrus.Extract(ctx).Data)
    34  		logProtoMessageAsJson(logEntry, req, "grpc.request.content", "server request payload logged as grpc.request.content field")
    35  		resp, err := handler(ctx, req)
    36  		if err == nil {
    37  			logProtoMessageAsJson(logEntry, resp, "grpc.response.content", "server response payload logged as grpc.request.content field")
    38  		}
    39  		return resp, err
    40  	}
    41  }
    42  
    43  // PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
    44  //
    45  // This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a
    46  // separate instance of the logger.
    47  func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpclogging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor {
    48  	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    49  		if !decider(stream.Context(), info.FullMethod, srv) {
    50  			return handler(srv, stream)
    51  		}
    52  		// Use the provided logrus.Entry for logging but use the fields from context.
    53  		// ctx_logrus.Extract is deprecated, use the ctxlogrus.Extract instead.
    54  		//logEntry := entry.WithFields(Extract(stream.Context()).Data)
    55  		logEntry := entry.WithFields(ctxlogrus.Extract(stream.Context()).Data)
    56  		newStream := &loggingServerStream{ServerStream: stream, entry: logEntry}
    57  		return handler(srv, newStream)
    58  	}
    59  }
    60  
    61  // PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses.
    62  func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpclogging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor {
    63  	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
    64  		if !decider(ctx, method) {
    65  			return invoker(ctx, method, req, reply, cc, opts...)
    66  		}
    67  		logEntry := entry.WithFields(newClientLoggerFields(ctx, method))
    68  		logProtoMessageAsJson(logEntry, req, "grpc.request.content", "client request payload logged as grpc.request.content")
    69  		err := invoker(ctx, method, req, reply, cc, opts...)
    70  		if err == nil {
    71  			logProtoMessageAsJson(logEntry, reply, "grpc.response.content", "client response payload logged as grpc.response.content")
    72  		}
    73  		return err
    74  	}
    75  }
    76  
    77  // PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses.
    78  func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpclogging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor {
    79  	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
    80  		if !decider(ctx, method) {
    81  			return streamer(ctx, desc, cc, method, opts...)
    82  		}
    83  		logEntry := entry.WithFields(newClientLoggerFields(ctx, method))
    84  		clientStream, err := streamer(ctx, desc, cc, method, opts...)
    85  		newStream := &loggingClientStream{ClientStream: clientStream, entry: logEntry}
    86  		return newStream, err
    87  	}
    88  }
    89  
    90  type loggingClientStream struct {
    91  	grpc.ClientStream
    92  	entry *logrus.Entry
    93  }
    94  
    95  func (l *loggingClientStream) SendMsg(m interface{}) error {
    96  	err := l.ClientStream.SendMsg(m)
    97  	if err == nil {
    98  		logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
    99  	}
   100  	return err
   101  }
   102  
   103  func (l *loggingClientStream) RecvMsg(m interface{}) error {
   104  	err := l.ClientStream.RecvMsg(m)
   105  	if err == nil {
   106  		logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
   107  	}
   108  	return err
   109  }
   110  
   111  type loggingServerStream struct {
   112  	grpc.ServerStream
   113  	entry *logrus.Entry
   114  }
   115  
   116  func (l *loggingServerStream) SendMsg(m interface{}) error {
   117  	err := l.ServerStream.SendMsg(m)
   118  	if err == nil {
   119  		logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field")
   120  	}
   121  	return err
   122  }
   123  
   124  func (l *loggingServerStream) RecvMsg(m interface{}) error {
   125  	err := l.ServerStream.RecvMsg(m)
   126  	if err == nil {
   127  		logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field")
   128  	}
   129  	return err
   130  }
   131  
   132  func logProtoMessageAsJson(entry *logrus.Entry, pbMsg interface{}, key string, msg string) {
   133  	if p, ok := pbMsg.(proto.Message); ok {
   134  		entry.WithField(key, &jsonpbMarshalleble{p}).Info(msg)
   135  	}
   136  }
   137  
   138  type jsonpbMarshalleble struct {
   139  	proto.Message
   140  }
   141  
   142  func (j *jsonpbMarshalleble) MarshalJSON() ([]byte, error) {
   143  	b := &bytes.Buffer{}
   144  	if err := JsonPbMarshaller.Marshal(b, j.Message); err != nil {
   145  		return nil, fmt.Errorf("jsonpb serializer failed: %v", err)
   146  	}
   147  	return b.Bytes(), nil
   148  }