Skip to content

Commit 413e750

Browse files
committed
logging
1 parent 0c46b2b commit 413e750

2 files changed

Lines changed: 77 additions & 61 deletions

File tree

grpc/interceptor/logging/logging.go

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -50,40 +50,27 @@ func UnaryClientInterceptor(logger logger, opts ...Option) grpc.UnaryClientInter
5050
return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, callOpts ...grpc.CallOption) (err error) {
5151
t := time.Now()
5252
o := newOpts(opts...)
53-
newCtx := ctx
54-
attrs := []any{}
55-
if o.logMethod {
56-
attrs = append(attrs, "grpc.method", method)
57-
}
58-
if o.logRequest {
59-
attrs = append(attrs, "grpc.request", req)
60-
}
61-
if len(attrs) > 0 {
62-
logger.Log(newCtx, slog.LevelInfo, "sent grpc request", attrs...)
63-
}
64-
if o.logResponse {
65-
defer func() {
66-
attrs := []any{}
67-
if o.logMethod {
68-
attrs = append(attrs, "grpc.method", method)
69-
}
70-
attrs = append(attrs, "grpc.response", reply)
71-
attrs = append(attrs, "grpc.error", err)
72-
if o.logDuration {
73-
attrs = append(attrs, "grpc.duration", time.Since(t).String())
74-
}
75-
76-
logger.Log(newCtx, slog.LevelInfo, "received grpc response", attrs...)
77-
}()
78-
}
53+
logRequest(logger, ctx, "sent grpc request", o, method, req)
54+
defer func() {
55+
logResponse(logger, ctx, "received grpc response", o, method, reply, err, time.Since(t))
56+
}()
7957
return invoker(ctx, method, req, reply, cc, callOpts...)
8058
}
8159
}
8260

61+
// StreamClientInterceptor returns a gRPC stream client interceptor that logs stream creation.
62+
// It uses the provided logger to log messages when a stream is created.
63+
// The behavior of the interceptor can be customized using options such as logging the method name and duration.
8364
func StreamClientInterceptor(logger logger, opts ...Option) grpc.StreamClientInterceptor {
84-
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, callOpts ...grpc.CallOption) (grpc.ClientStream, error) {
85-
// TODO implement logging for streaming RPCs
86-
return streamer(ctx, desc, cc, method, callOpts...)
65+
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, callOpts ...grpc.CallOption) (stream grpc.ClientStream, err error) {
66+
t := time.Now()
67+
o := newOpts(opts...)
68+
logRequest(logger, ctx, "creating grpc client stream", o, method, nil)
69+
defer func() {
70+
logResponse(logger, ctx, "created grpc client stream", o, method, nil, err, time.Since(t))
71+
}()
72+
stream, err = streamer(ctx, desc, cc, method, callOpts...)
73+
return stream, err
8774
}
8875
}
8976

@@ -95,41 +82,29 @@ func UnaryServerInterceptor(logger logger, opts ...Option) grpc.UnaryServerInter
9582
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (res any, err error) {
9683
t := time.Now()
9784
o := newOpts(opts...)
98-
newCtx := ctx
99-
attrs := []any{}
100-
if o.logMethod {
101-
attrs = append(attrs, "grpc.method", info.FullMethod)
102-
}
103-
if o.logRequest {
104-
attrs = append(attrs, "grpc.request", req)
105-
}
106-
if len(attrs) > 0 {
107-
logger.Log(newCtx, slog.LevelInfo, "received grpc request", attrs...)
108-
}
109-
if o.logResponse {
110-
defer func() {
111-
attrs := []any{}
112-
if o.logMethod {
113-
attrs = append(attrs, "grpc.method", info.FullMethod)
114-
}
115-
attrs = append(attrs, "grpc.response", res)
116-
attrs = append(attrs, "grpc.error", err)
117-
if o.logDuration {
118-
attrs = append(attrs, "grpc.duration", time.Since(t).String())
119-
}
120-
121-
logger.Log(newCtx, slog.LevelInfo, "sent grpc response", attrs...)
122-
}()
123-
}
124-
res, err = handler(newCtx, req)
85+
logRequest(logger, ctx, "received grpc request", o, info.FullMethod, req)
86+
defer func() {
87+
logResponse(logger, ctx, "sent grpc response", o, info.FullMethod, res, err, time.Since(t))
88+
}()
89+
res, err = handler(ctx, req)
12590
return res, err
12691
}
12792
}
12893

94+
// StreamServerInterceptor returns a gRPC stream server interceptor that logs stream creation.
95+
// It uses the provided logger to log messages when a stream is created.
96+
// The behavior of the interceptor can be customized using options such as logging the method name and duration.
12997
func StreamServerInterceptor(logger logger, opts ...Option) grpc.StreamServerInterceptor {
130-
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
131-
// TODO implement logging for streaming RPCs
132-
return handler(srv, ss)
98+
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
99+
t := time.Now()
100+
o := newOpts(opts...)
101+
ctx := ss.Context()
102+
logRequest(logger, ctx, "creating grpc server stream", o, info.FullMethod, nil)
103+
defer func() {
104+
logResponse(logger, ctx, "created grpc server stream", o, info.FullMethod, nil, err, time.Since(t))
105+
}()
106+
err = handler(srv, ss)
107+
return err
133108
}
134109
}
135110

@@ -167,3 +142,29 @@ func contextLogger(appendToContext AppendToContextFunc, metaFunc func(ctx contex
167142
return newCtx, nil
168143
}
169144
}
145+
146+
// logRequest logs the initial request with the specified method and request data.
147+
func logRequest(logger logger, ctx context.Context, msg string, o *options, method string, req any) {
148+
attrs := []any{}
149+
if o.logMethod {
150+
attrs = append(attrs, "grpc.method", method)
151+
}
152+
if o.logRequest {
153+
attrs = append(attrs, "grpc.request", req)
154+
}
155+
logger.Log(ctx, slog.LevelInfo, msg, attrs...)
156+
}
157+
158+
// logResponse logs the response with the specified method, response data, error, and duration.
159+
func logResponse(logger logger, ctx context.Context, msg string, o *options, method string, res any, err error, duration time.Duration) {
160+
attrs := []any{}
161+
if o.logMethod {
162+
attrs = append(attrs, "grpc.method", method)
163+
}
164+
attrs = append(attrs, "grpc.response", res)
165+
attrs = append(attrs, "grpc.error", err)
166+
if o.logDuration {
167+
attrs = append(attrs, "grpc.duration", duration.String())
168+
}
169+
logger.Log(ctx, slog.LevelInfo, msg, attrs...)
170+
}

grpc/interceptor/logging/options.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package logging
22

3-
type (
4-
Option func(*options)
3+
// Option is a function that configures logging interceptor options.
4+
type Option func(*options)
55

6+
type (
67
options struct {
78
logMethod bool
89
logRequest bool
@@ -11,34 +12,48 @@ type (
1112
}
1213
)
1314

15+
// Method returns an Option that enables or disables logging of gRPC method names.
16+
// When called without arguments or with true, it enables method logging.
17+
// When called with false, it disables method logging.
1418
func Method(enabled ...bool) Option {
1519
enable := len(enabled) == 0 || len(enabled) > 0 && enabled[0]
1620
return func(o *options) {
1721
o.logMethod = enable
1822
}
1923
}
2024

25+
// Request returns an Option that enables or disables logging of gRPC request data.
26+
// When called without arguments or with true, it enables request logging.
27+
// When called with false, it disables request logging.
2128
func Request(enabled ...bool) Option {
2229
enable := len(enabled) == 0 || len(enabled) > 0 && enabled[0]
2330
return func(o *options) {
2431
o.logRequest = enable
2532
}
2633
}
2734

35+
// Response returns an Option that enables or disables logging of gRPC response data.
36+
// When called without arguments or with true, it enables response logging.
37+
// When called with false, it disables response logging.
2838
func Response(enabled ...bool) Option {
2939
enable := len(enabled) == 0 || len(enabled) > 0 && enabled[0]
3040
return func(o *options) {
3141
o.logResponse = enable
3242
}
3343
}
3444

45+
// Duration returns an Option that enables or disables logging of gRPC call duration.
46+
// When called without arguments or with true, it enables duration logging.
47+
// When called with false, it disables duration logging.
3548
func Duration(enabled ...bool) Option {
3649
enable := len(enabled) == 0 || len(enabled) > 0 && enabled[0]
3750
return func(o *options) {
3851
o.logDuration = enable
3952
}
4053
}
4154

55+
// All returns an Option that enables all logging options:
56+
// method, request, response, and duration.
4257
func All() Option {
4358
return func(o *options) {
4459
o.logRequest = true

0 commit comments

Comments
 (0)