Skip to content

Commit

Permalink
v2:interceptors/logging: add payload decision to separate request res…
Browse files Browse the repository at this point in the history
…ponse payload logging
  • Loading branch information
michaljemala committed Aug 7, 2021
1 parent dc87da6 commit 9f260f9
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 25 deletions.
20 changes: 17 additions & 3 deletions interceptors/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,29 @@ func DefaultDeciderMethod(_ string, _ error) Decision {
return LogStartAndFinishCall
}

// PayloadDecision defines rules for enabling payload logging of request and responses.
type PayloadDecision int

const (
// NoPayloadLogging - Payload logging is disabled.
NoPayloadLogging PayloadDecision = iota
// LogPayloadRequest - Only logging of requests is enabled.
LogPayloadRequest
// LogPayloadResponse - Only logging of responses is enabled.
LogPayloadResponse
// LogPayloadRequestAndResponse - Logging of both requests and responses is enabled.
LogPayloadRequestAndResponse
)

// ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side
// request/response payloads
type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool
type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) PayloadDecision

// ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side
// request/response payloads
type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool
type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) PayloadDecision

// JsonPbMarshaller is a marshaller that serializes protobuf messages.
// JsonPbMarshaler is a marshaller that serializes protobuf messages.
type JsonPbMarshaler interface {
Marshal(pb proto.Message) ([]byte, error)
}
Expand Down
84 changes: 64 additions & 20 deletions interceptors/logging/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import (
)

type serverPayloadReporter struct {
ctx context.Context
logger Logger
ctx context.Context
logger Logger
decision PayloadDecision
}

func (c *serverPayloadReporter) PostCall(error, time.Duration) {}
Expand All @@ -28,6 +29,11 @@ func (c *serverPayloadReporter) PostMsgSend(req interface{}, err error, duration
if err != nil {
return
}
switch c.decision {
case LogPayloadResponse, LogPayloadRequestAndResponse:
default:
return
}

logger := c.logger.With(extractFields(tags.Extract(c.ctx))...)
p, ok := req.(proto.Message)
Expand All @@ -44,6 +50,11 @@ func (c *serverPayloadReporter) PostMsgReceive(reply interface{}, err error, dur
if err != nil {
return
}
switch c.decision {
case LogPayloadRequest, LogPayloadRequestAndResponse:
default:
return
}

logger := c.logger.With(extractFields(tags.Extract(c.ctx))...)

Expand All @@ -57,8 +68,9 @@ func (c *serverPayloadReporter) PostMsgReceive(reply interface{}, err error, dur
}

type clientPayloadReporter struct {
ctx context.Context
logger Logger
ctx context.Context
logger Logger
decision PayloadDecision
}

func (c *clientPayloadReporter) PostCall(error, time.Duration) {}
Expand All @@ -67,6 +79,11 @@ func (c *clientPayloadReporter) PostMsgSend(req interface{}, err error, duration
if err != nil {
return
}
switch c.decision {
case LogPayloadRequest, LogPayloadRequestAndResponse:
default:
return
}

logger := c.logger.With(extractFields(tags.Extract(c.ctx))...)
p, ok := req.(proto.Message)
Expand All @@ -81,6 +98,11 @@ func (c *clientPayloadReporter) PostMsgReceive(reply interface{}, err error, dur
if err != nil {
return
}
switch c.decision {
case LogPayloadResponse, LogPayloadRequestAndResponse:
default:
return
}

logger := c.logger.With(extractFields(tags.Extract(c.ctx))...)
p, ok := reply.(proto.Message)
Expand All @@ -98,8 +120,10 @@ type payloadReportable struct {
timestampFormat string
}

func (r *payloadReportable) ServerReporter(ctx context.Context, req interface{}, typ interceptors.GRPCType, service string, method string) (interceptors.Reporter, context.Context) {
if !r.serverDecider(ctx, interceptors.FullMethod(service, method), req) {
func (r *payloadReportable) ServerReporter(ctx context.Context, req interface{}, typ interceptors.GRPCType,
service string, method string) (interceptors.Reporter, context.Context) {
decision := r.serverDecider(ctx, interceptors.FullMethod(service, method), req)
if decision == NoPayloadLogging {
return interceptors.NoopReporter{}, ctx
}
fields := commonFields(KindServerFieldValue, typ, service, method)
Expand All @@ -108,12 +132,15 @@ func (r *payloadReportable) ServerReporter(ctx context.Context, req interface{},
fields = append(fields, "grpc.request.deadline", d.Format(r.timestampFormat))
}
return &serverPayloadReporter{
ctx: ctx,
logger: r.logger.With(fields...),
ctx: ctx,
logger: r.logger.With(fields...),
decision: decision,
}, ctx
}
func (r *payloadReportable) ClientReporter(ctx context.Context, _ interface{}, typ interceptors.GRPCType, service string, method string) (interceptors.Reporter, context.Context) {
if !r.clientDecider(ctx, interceptors.FullMethod(service, method)) {
func (r *payloadReportable) ClientReporter(ctx context.Context, _ interface{}, typ interceptors.GRPCType,
service string, method string) (interceptors.Reporter, context.Context) {
decision := r.clientDecider(ctx, interceptors.FullMethod(service, method))
if decision == NoPayloadLogging {
return interceptors.NoopReporter{}, ctx
}
fields := commonFields(KindClientFieldValue, typ, service, method)
Expand All @@ -122,33 +149,50 @@ func (r *payloadReportable) ClientReporter(ctx context.Context, _ interface{}, t
fields = append(fields, "grpc.request.deadline", d.Format(r.timestampFormat))
}
return &clientPayloadReporter{
ctx: ctx,
logger: r.logger.With(fields...),
ctx: ctx,
logger: r.logger.With(fields...),
decision: decision,
}, ctx
}

// PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests on INFO level.
// Logger tags will be used from tags context.
func PayloadUnaryServerInterceptor(logger Logger, decider ServerPayloadLoggingDecider, timestampFormat string) grpc.UnaryServerInterceptor {
return interceptors.UnaryServerInterceptor(&payloadReportable{logger: logger, serverDecider: decider, timestampFormat: timestampFormat})
func PayloadUnaryServerInterceptor(logger Logger, decider ServerPayloadLoggingDecider,
timestampFormat string) grpc.UnaryServerInterceptor {
return interceptors.UnaryServerInterceptor(&payloadReportable{
logger: logger,
serverDecider: decider,
timestampFormat: timestampFormat})
}

// PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests on INFO level.
// Logger tags will be used from tags context.
func PayloadStreamServerInterceptor(logger Logger, decider ServerPayloadLoggingDecider, timestampFormat string) grpc.StreamServerInterceptor {
return interceptors.StreamServerInterceptor(&payloadReportable{logger: logger, serverDecider: decider, timestampFormat: timestampFormat})
func PayloadStreamServerInterceptor(logger Logger, decider ServerPayloadLoggingDecider,
timestampFormat string) grpc.StreamServerInterceptor {
return interceptors.StreamServerInterceptor(&payloadReportable{
logger: logger,
serverDecider: decider,
timestampFormat: timestampFormat})
}

// PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the payloads of requests and responses on INFO level.
// Logger tags will be used from tags context.
func PayloadUnaryClientInterceptor(logger Logger, decider ClientPayloadLoggingDecider, timestampFormat string) grpc.UnaryClientInterceptor {
return interceptors.UnaryClientInterceptor(&payloadReportable{logger: logger, clientDecider: decider, timestampFormat: timestampFormat})
func PayloadUnaryClientInterceptor(logger Logger, decider ClientPayloadLoggingDecider,
timestampFormat string) grpc.UnaryClientInterceptor {
return interceptors.UnaryClientInterceptor(&payloadReportable{
logger: logger,
clientDecider: decider,
timestampFormat: timestampFormat})
}

// PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses on INFO level.
// Logger tags will be used from tags context.
func PayloadStreamClientInterceptor(logger Logger, decider ClientPayloadLoggingDecider, timestampFormat string) grpc.StreamClientInterceptor {
return interceptors.StreamClientInterceptor(&payloadReportable{logger: logger, clientDecider: decider, timestampFormat: timestampFormat})
func PayloadStreamClientInterceptor(logger Logger, decider ClientPayloadLoggingDecider,
timestampFormat string) grpc.StreamClientInterceptor {
return interceptors.StreamClientInterceptor(&payloadReportable{
logger: logger,
clientDecider: decider,
timestampFormat: timestampFormat})
}

func logProtoMessageAsJson(logger Logger, pbMsg proto.Message, key string, msg string) {
Expand Down
8 changes: 6 additions & 2 deletions interceptors/logging/payload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ type loggingPayloadSuite struct {
}

func TestPayloadSuite(t *testing.T) {
alwaysLoggingDeciderServer := func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { return true }
alwaysLoggingDeciderClient := func(ctx context.Context, fullMethodName string) bool { return true }
alwaysLoggingDeciderServer := func(context.Context, string, interface{}) logging.PayloadDecision {
return logging.LogPayloadRequestAndResponse
}
alwaysLoggingDeciderClient := func(context.Context, string) logging.PayloadDecision {
return logging.LogPayloadRequestAndResponse
}

s := &loggingPayloadSuite{
baseLoggingSuite: &baseLoggingSuite{
Expand Down

0 comments on commit 9f260f9

Please sign in to comment.