Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request Logging #311

Merged
merged 31 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
faf7951
add initial setup for the request logger
yashrsharma44 Jun 29, 2020
763ae6a
compose the request logger in the original logger itself
yashrsharma44 Jul 1, 2020
cca01b8
modify the tests to use the new param signature
yashrsharma44 Jul 1, 2020
c0bba98
configure the option to enable or disable request logger
yashrsharma44 Jul 1, 2020
af4498e
added logging of metadata of the request object
yashrsharma44 Jul 1, 2020
ac3c229
nitpickings
yashrsharma44 Jul 1, 2020
48d0d13
added pre call implementation
yashrsharma44 Jul 6, 2020
3571e6d
Nitpickings
yashrsharma44 Jul 7, 2020
785d4d2
Removed the request decider and added another option for enabling the…
yashrsharma44 Jul 13, 2020
b392e8e
Removed the decider from the tests
yashrsharma44 Jul 13, 2020
a70e2b5
Apply suggestions from code review
yashrsharma44 Jul 13, 2020
1cd2cd7
Added logging of start and end messages of the request and response f…
yashrsharma44 Jul 13, 2020
80972cd
Added a decider for enabling request logging
yashrsharma44 Jul 13, 2020
247eb4e
Added level of logging and contextual message for identifying the sta…
yashrsharma44 Jul 13, 2020
f7a66a3
add more fields to record the start and end of the call
yashrsharma44 Jul 13, 2020
87619bb
modified the logging message
yashrsharma44 Jul 13, 2020
cf1d506
Added tests for the request logging
yashrsharma44 Jul 13, 2020
d35941e
Added changes as suggested by Bartek
yashrsharma44 Jul 13, 2020
970c8a1
Reverted logging strategy
yashrsharma44 Jul 20, 2020
8b46a66
Added underscore for proper spacing and added grpc.error for protocol…
yashrsharma44 Jul 22, 2020
068986d
Changes as requested by the reviewer -
yashrsharma44 Jul 23, 2020
80226df
Modified the tests for the new API
yashrsharma44 Jul 23, 2020
db3f9eb
nitpickings
yashrsharma44 Jul 23, 2020
50d4c5c
Multiple changes
yashrsharma44 Jul 24, 2020
80f092a
Added tests for the finish call\n*Test the finish call in every test …
yashrsharma44 Jul 24, 2020
3de7e81
Modified the decider to use only method for deciding the logging
yashrsharma44 Jul 24, 2020
c202e72
Added ENUM based decider for logging
yashrsharma44 Jul 24, 2020
76e8e58
added some comments for usage of decider
yashrsharma44 Jul 24, 2020
ac1f12b
renamed LogAllCall to LogStartAndFinishCall
yashrsharma44 Jul 24, 2020
88ee96c
remove unused comments
yashrsharma44 Jul 24, 2020
fbc8146
modified the examples in the provider
yashrsharma44 Oct 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 47 additions & 25 deletions interceptors/logging/interceptors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,55 @@ type reporter struct {
ctx context.Context
typ interceptors.GRPCType
service, method string
startCallLogged bool
opts *options
logger Logger
kind string
}

opts *options
logger Logger

kind string
func (c *reporter) logMessage(logger Logger, err error, msg string, duration time.Duration) {
code := c.opts.codeFunc(err)
yashrsharma44 marked this conversation as resolved.
Show resolved Hide resolved
logger = logger.With("grpc.code", code.String())
if err != nil {
logger = logger.With("grpc.error", fmt.Sprintf("%v", err))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
logger = logger.With("grpc.error", fmt.Sprintf("%v", err))
logger = logger.With("grpc.error", err.Error())

}
logger = logger.With(extractFields(tags.Extract(c.ctx))...)
logger.With(c.opts.durationFieldFunc(duration)...).Log(c.opts.levelFunc(code), msg)
}

func (c *reporter) PostCall(err error, duration time.Duration) {
if !c.opts.shouldLog(interceptors.FullMethod(c.service, c.method), err) {
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method)) {
case LogFinishCall, LogStartAndFinishCall:
if err == io.EOF {
err = nil
}
c.logMessage(c.logger, err, "finished call", duration)
default:
return
}
if err == io.EOF {
err = nil
}
// Get optional, fresh tags.
logger := c.logger.With(extractFields(tags.Extract(c.ctx))...)
}

code := c.opts.codeFunc(err)
logger = logger.With("grpc.code", code.String())
if err != nil {
logger = logger.With("error", fmt.Sprintf("%v", err))
func (c *reporter) PostMsgSend(_ interface{}, err error, duration time.Duration) {
if c.startCallLogged {
return
}
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method)) {
case LogStartAndFinishCall:
c.startCallLogged = true
c.logMessage(c.logger, err, "started call", duration)
}
logger.With(c.opts.durationFieldFunc(duration)...).Log(c.opts.levelFunc(code), fmt.Sprintf("finished %s %s call", c.kind, c.typ))
}

yashrsharma44 marked this conversation as resolved.
Show resolved Hide resolved
func (c *reporter) PostMsgSend(interface{}, error, time.Duration) {}

func (c *reporter) PostMsgReceive(interface{}, error, time.Duration) {}
func (c *reporter) PostMsgReceive(_ interface{}, err error, duration time.Duration) {
if c.startCallLogged {
return
}
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method)) {
case LogStartAndFinishCall:
c.startCallLogged = true
c.logMessage(c.logger, err, "started call", duration)
}
}

type reportable struct {
opts *options
Expand All @@ -74,13 +95,14 @@ func (r *reportable) reporter(ctx context.Context, typ interceptors.GRPCType, se
fields = append(fields, "grpc.request.deadline", d.Format(time.RFC3339))
}
return &reporter{
ctx: ctx,
typ: typ,
service: service,
method: method,
opts: r.opts,
logger: r.logger.With(fields...),
kind: kind,
ctx: ctx,
typ: typ,
service: service,
method: method,
startCallLogged: false,
opts: r.opts,
logger: r.logger.With(fields...),
kind: kind,
}, ctx
}

Expand Down
214 changes: 132 additions & 82 deletions interceptors/logging/interceptors_test.go

Large diffs are not rendered by default.

25 changes: 19 additions & 6 deletions interceptors/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
)

// Decision defines rules for enabling start and end of logging.
type Decision int
yashrsharma44 marked this conversation as resolved.
Show resolved Hide resolved

const (
// NoLogCall - Logging is disabled.
NoLogCall Decision = iota
yashrsharma44 marked this conversation as resolved.
Show resolved Hide resolved
// LogFinishCall - Only finish logs of request is enabled.
LogFinishCall
// LogStartAndFinishCall - Logging of start and end of request is enabled.
LogStartAndFinishCall
)

var (
// SystemTag is tag representing an event inside gRPC call.
SystemTag = []string{"system", "grpc"}
KindFieldKey = "kind"
SystemTag = []string{"protocol", "grpc"}
// ComponentFieldKey is a tag representing the client/server that is calling.
ComponentFieldKey = "grpc.component"
KindServerFieldValue = "server"
KindClientFieldValue = "client"
ServiceFieldKey = "grpc.service"
Expand All @@ -28,7 +41,7 @@ var (
func commonFields(kind string, typ interceptors.GRPCType, service string, method string) Fields {
return Fields{
SystemTag[0], SystemTag[1],
KindFieldKey, kind,
ComponentFieldKey, kind,
ServiceFieldKey, service,
MethodFieldKey, method,
MethodTypeFieldKey, string(typ),
Expand All @@ -47,12 +60,12 @@ func DefaultErrorToCode(err error) codes.Code {
}

// Decider function defines rules for suppressing any interceptor logs
type Decider func(fullMethodName string, err error) bool
type Decider func(fullMethodName string) Decision
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would have been nice not to have skipped the error parameter here. The logging can also be configured using the error as well,(since status code can be derived from the response error).


// DefaultDeciderMethod is the default implementation of decider to see if you should log the call
// by default this if always true so all calls are logged
func DefaultDeciderMethod(_ string, _ error) bool {
return true
func DefaultDeciderMethod(_ string) Decision {
return LogStartAndFinishCall
}

// ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side
Expand Down
9 changes: 4 additions & 5 deletions providers/kit/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,13 @@ func ExampleWithDecider() {
logger := log.NewNopLogger()
// Shared options for the logger, with a custom decider that log everything except successful calls from "/blah.foo.healthcheck/Check" method.
opts := []logging.Option{
logging.WithDecider(func(methodFullName string, err error) bool {
logging.WithDecider(func(methodFullName string) logging.Decision {
// will not log gRPC calls if it was a call to healthcheck and no error was raised
if err == nil && methodFullName == "/blah.foo.healthcheck/Check" {
return false
if methodFullName == "/blah.foo.healthcheck/Check" {
return logging.NoLogCall
}

// by default you will log all calls
return true
return logging.LogStartAndFinishCall
}),
}
// Create a server, make sure we put the tags context before everything else.
Expand Down
8 changes: 4 additions & 4 deletions providers/logrus/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ func ExampleWithDecider() {
logger := logrus.New()
// Shared options for the logger, with a custom decider that log everything except successful calls from "/blah.foo.healthcheck/Check" method.
opts := []logging.Option{
logging.WithDecider(func(methodFullName string, err error) bool {
logging.WithDecider(func(methodFullName string) logging.Decision {
// will not log gRPC calls if it was a call to healthcheck and no error was raised
if err == nil && methodFullName == "/blah.foo.healthcheck/Check" {
return false
if methodFullName == "/blah.foo.healthcheck/Check" {
return logging.NoLogCall
}

// by default you will log all calls
return true
return logging.LogStartAndFinishCall
}),
}
// Create a server, make sure we put the tags context before everything else.
Expand Down
8 changes: 4 additions & 4 deletions providers/zap/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ func ExampleWithDecider() {
logger := zap.NewNop()
// Shared options for the logger, with a custom decider that log everything except successful calls from "/blah.foo.healthcheck/Check" method.
opts := []logging.Option{
logging.WithDecider(func(methodFullName string, err error) bool {
logging.WithDecider(func(methodFullName string) logging.Decision {
// will not log gRPC calls if it was a call to healthcheck and no error was raised
if err == nil && methodFullName == "/blah.foo.healthcheck/Check" {
return false
if methodFullName == "/blah.foo.healthcheck/Check" {
return logging.NoLogCall
}

// by default you will log all calls
return true
return logging.LogStartAndFinishCall
}),
}
// Create a server, make sure we put the tags context before everything else.
Expand Down
8 changes: 4 additions & 4 deletions providers/zerolog/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ func ExampleWithDecider() {
// Shared options for the logger, with a custom decider that log everything except successful
// calls from "/blah.foo.healthcheck/Check" method.
opts := []logging.Option{
logging.WithDecider(func(methodFullName string, err error) bool {
logging.WithDecider(func(methodFullName string) logging.Decision {
// will not log gRPC calls if it was a call to healthcheck and no error was raised
if err == nil && methodFullName == "/blah.foo.healthcheck/Check" {
return false
if methodFullName == "/blah.foo.healthcheck/Check" {
return logging.NoLogCall
}

// by default you will log all calls
return true
return logging.LogStartAndFinishCall
}),
}
// Create a server, make sure we put the tags context before everything else.
Expand Down