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 }