github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-grpc-middleware/logging/zap/payload_interceptors.go (about)

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