Skip to content

Commit f8899ff

Browse files
authored
Removed tags; Simplified interceptor code; Added logging fields editability. (#394)
* Removed tags; Simplified interceptor code; Added logging fields editability. Fixes #382 Signed-off-by: Bartlomiej Plotka <[email protected]> Removed tags; Simplified interceptor code; Added logging fields editability. Fixes #382 Signed-off-by: Bartlomiej Plotka <[email protected]> Fixed tests. Signed-off-by: Bartlomiej Plotka <[email protected]> Fixed open metrics test. Signed-off-by: Bartlomiej Plotka <[email protected]> * Addressed comments. Signed-off-by: Bartlomiej Plotka <[email protected]>
1 parent 274df59 commit f8899ff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+585
-2009
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,4 @@ coverage.txt
202202
vendor/
203203

204204
.envrc
205-
.bin
205+
.bin

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Types of changes:
1212
- `Fixed` for any bug fixes.
1313
- `Security` in case of vulnerabilities.
1414

15+
## v2
16+
17+
### Changed
18+
19+
* `tags` removed. Use `logging.ExtractFields` to read logging fields from logging interceptor for your local request logger. Use `logging.InjectFields` to inject custom fields to logging interceptor to client context or interceptor before logging interceptor.
20+
1521
## [Unreleased]
1622

1723
### Added

interceptors/auth/examples_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"google.golang.org/grpc/status"
1414

1515
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
16-
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
16+
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
1717
)
1818

1919
var tokenInfoKey struct{}
@@ -38,12 +38,10 @@ func exampleAuthFunc(ctx context.Context) (context.Context, error) {
3838
return nil, status.Errorf(codes.Unauthenticated, "invalid auth token: %v", err)
3939
}
4040

41-
tags.Extract(ctx).Set("auth.sub", userClaimFromToken(tokenInfo))
41+
ctx = logging.InjectFields(ctx, logging.Fields{"auth.sub", userClaimFromToken(tokenInfo)})
4242

43-
// WARNING: in production define your own type to avoid context collisions
44-
newCtx := context.WithValue(ctx, tokenInfoKey, tokenInfo)
45-
46-
return newCtx, nil
43+
// WARNING: In production define your own type to avoid context collisions.
44+
return context.WithValue(ctx, tokenInfoKey, tokenInfo), nil
4745
}
4846

4947
// Simple example of server initialization code

interceptors/client.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ import (
1717
func UnaryClientInterceptor(reportable ClientReportable) grpc.UnaryClientInterceptor {
1818
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
1919
r := newReport(Unary, method)
20-
reporter, newCtx := reportable.ClientReporter(ctx, req, r.rpcType, r.service, r.method)
20+
reporter, newCtx := reportable.ClientReporter(ctx, CallMeta{ReqProtoOrNil: req, Typ: r.rpcType, Service: r.service, Method: r.method})
2121

2222
reporter.PostMsgSend(req, nil, time.Since(r.startTime))
2323
err := invoker(newCtx, method, req, reply, cc, opts...)
2424
reporter.PostMsgReceive(reply, err, time.Since(r.startTime))
25-
2625
reporter.PostCall(err, time.Since(r.startTime))
2726
return err
2827
}
@@ -32,7 +31,7 @@ func UnaryClientInterceptor(reportable ClientReportable) grpc.UnaryClientInterce
3231
func StreamClientInterceptor(reportable ClientReportable) grpc.StreamClientInterceptor {
3332
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
3433
r := newReport(clientStreamType(desc), method)
35-
reporter, newCtx := reportable.ClientReporter(ctx, nil, r.rpcType, r.service, r.method)
34+
reporter, newCtx := reportable.ClientReporter(ctx, CallMeta{ReqProtoOrNil: nil, Typ: r.rpcType, Service: r.service, Method: r.method})
3635

3736
clientStream, err := streamer(newCtx, desc, cc, method, opts...)
3837
if err != nil {

interceptors/client_test.go

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import (
2121
)
2222

2323
type mockReport struct {
24-
typ GRPCType
25-
svcName, methodName string
24+
CallMeta
2625

2726
postCalls []error
2827
postMsgSends []error
@@ -38,11 +37,13 @@ type mockReportable struct {
3837

3938
// Equal replaces require.Equal as google.golang.org/grpc/status errors are not easily comparable.
4039
func (m *mockReportable) Equal(t *testing.T, expected []*mockReport) {
40+
t.Helper()
41+
4142
require.Len(t, expected, len(m.reports))
4243
for i, e := range m.reports {
43-
require.Equal(t, expected[i].typ, e.typ, "%v", i)
44-
require.Equal(t, expected[i].svcName, e.svcName, "%v", i)
45-
require.Equal(t, expected[i].methodName, e.methodName, "%v", i)
44+
require.Equal(t, expected[i].Typ, e.Typ, "%v", i)
45+
require.Equal(t, expected[i].Service, e.Service, "%v", i)
46+
require.Equal(t, expected[i].Method, e.Method, "%v", i)
4647

4748
require.Len(t, expected[i].postCalls, len(e.postCalls), "%v", i)
4849
for k, err := range e.postCalls {
@@ -111,14 +112,14 @@ func (m *mockReportable) PostMsgReceive(_ interface{}, err error, _ time.Duratio
111112
m.curr.postMsgReceives = append(m.curr.postMsgReceives, err)
112113
}
113114

114-
func (m *mockReportable) ClientReporter(ctx context.Context, _ interface{}, typ GRPCType, serviceName string, methodName string) (Reporter, context.Context) {
115-
m.curr = &mockReport{typ: typ, svcName: serviceName, methodName: methodName}
115+
func (m *mockReportable) ClientReporter(ctx context.Context, c CallMeta) (Reporter, context.Context) {
116+
m.curr = &mockReport{CallMeta: c}
116117
m.reports = append(m.reports, m.curr)
117118
return m, ctx
118119
}
119120

120-
func (m *mockReportable) ServerReporter(ctx context.Context, _ interface{}, typ GRPCType, serviceName string, methodName string) (Reporter, context.Context) {
121-
m.curr = &mockReport{typ: typ, svcName: serviceName, methodName: methodName}
121+
func (m *mockReportable) ServerReporter(ctx context.Context, c CallMeta) (Reporter, context.Context) {
122+
m.curr = &mockReport{CallMeta: c}
122123
m.reports = append(m.reports, m.curr)
123124
return m, ctx
124125
}
@@ -206,9 +207,7 @@ func (s *ClientInterceptorTestSuite) TestUnaryReporting() {
206207
_, err := s.testClient.PingEmpty(s.ctx, &testpb.PingEmptyRequest{}) // should return with code=OK
207208
require.NoError(s.T(), err)
208209
s.mock.Equal(s.T(), []*mockReport{{
209-
typ: Unary,
210-
svcName: testpb.TestServiceFullName,
211-
methodName: "PingEmpty",
210+
CallMeta: CallMeta{Typ: Unary, Service: testpb.TestServiceFullName, Method: "PingEmpty"},
212211
postCalls: []error{nil},
213212
postMsgReceives: []error{nil},
214213
postMsgSends: []error{nil},
@@ -218,9 +217,7 @@ func (s *ClientInterceptorTestSuite) TestUnaryReporting() {
218217
_, err = s.testClient.PingError(s.ctx, &testpb.PingErrorRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
219218
require.Error(s.T(), err)
220219
s.mock.Equal(s.T(), []*mockReport{{
221-
typ: Unary,
222-
svcName: testpb.TestServiceFullName,
223-
methodName: "PingError",
220+
CallMeta: CallMeta{Typ: Unary, Service: testpb.TestServiceFullName, Method: "PingError"},
224221
postCalls: []error{status.Errorf(codes.FailedPrecondition, "Userspace error.")},
225222
postMsgReceives: []error{status.Errorf(codes.FailedPrecondition, "Userspace error.")},
226223
postMsgSends: []error{nil},
@@ -233,9 +230,7 @@ func (s *ClientInterceptorTestSuite) TestStartedListReporting() {
233230

234231
// Even without reading, we should get initial mockReport.
235232
s.mock.Equal(s.T(), []*mockReport{{
236-
typ: ServerStream,
237-
svcName: testpb.TestServiceFullName,
238-
methodName: "PingList",
233+
CallMeta: CallMeta{Typ: ServerStream, Service: testpb.TestServiceFullName, Method: "PingList"},
239234
postMsgSends: []error{nil},
240235
}})
241236

@@ -244,14 +239,10 @@ func (s *ClientInterceptorTestSuite) TestStartedListReporting() {
244239

245240
// Even without reading, we should get initial mockReport.
246241
s.mock.Equal(s.T(), []*mockReport{{
247-
typ: ServerStream,
248-
svcName: testpb.TestServiceFullName,
249-
methodName: "PingList",
242+
CallMeta: CallMeta{Typ: ServerStream, Service: testpb.TestServiceFullName, Method: "PingList"},
250243
postMsgSends: []error{nil},
251244
}, {
252-
typ: ServerStream,
253-
svcName: testpb.TestServiceFullName,
254-
methodName: "PingList",
245+
CallMeta: CallMeta{Typ: ServerStream, Service: testpb.TestServiceFullName, Method: "PingList"},
255246
postMsgSends: []error{nil},
256247
}})
257248
}
@@ -273,9 +264,7 @@ func (s *ClientInterceptorTestSuite) TestListReporting() {
273264
require.EqualValues(s.T(), testpb.ListResponseCount, count, "Number of received msg on the wire must match")
274265

275266
s.mock.Equal(s.T(), []*mockReport{{
276-
typ: ServerStream,
277-
svcName: testpb.TestServiceFullName,
278-
methodName: "PingList",
267+
CallMeta: CallMeta{Typ: ServerStream, Service: testpb.TestServiceFullName, Method: "PingList"},
279268
postCalls: []error{nil},
280269
postMsgReceives: append(make([]error, testpb.ListResponseCount), io.EOF),
281270
postMsgSends: []error{nil},
@@ -298,9 +287,7 @@ func (s *ClientInterceptorTestSuite) TestListReporting() {
298287
require.Equal(s.T(), codes.FailedPrecondition, st.Code(), "Recv must return FailedPrecondition, otherwise the test is wrong")
299288

300289
s.mock.Equal(s.T(), []*mockReport{{
301-
typ: ServerStream,
302-
svcName: testpb.TestServiceFullName,
303-
methodName: "PingList",
290+
CallMeta: CallMeta{Typ: ServerStream, Service: testpb.TestServiceFullName, Method: "PingList"},
304291
postCalls: []error{status.Errorf(codes.FailedPrecondition, "foobar"), status.Errorf(codes.FailedPrecondition, "foobar")},
305292
postMsgReceives: []error{status.Errorf(codes.FailedPrecondition, "foobar"), status.Errorf(codes.FailedPrecondition, "foobar")},
306293
postMsgSends: []error{nil},
@@ -344,9 +331,7 @@ func (s *ClientInterceptorTestSuite) TestBiStreamingReporting() {
344331

345332
require.EqualValues(s.T(), 100, count, "Number of received msg on the wire must match")
346333
s.mock.Equal(s.T(), []*mockReport{{
347-
typ: BidiStream,
348-
svcName: testpb.TestServiceFullName,
349-
methodName: "PingStream",
334+
CallMeta: CallMeta{Typ: BidiStream, Service: testpb.TestServiceFullName, Method: "PingStream"},
350335
postCalls: []error{nil},
351336
postMsgReceives: append(make([]error, 100), io.EOF),
352337
postMsgSends: make([]error, 100),

interceptors/logging/doc.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
// Licensed under the Apache License 2.0.
33

44
/*
5-
logging is a "parent" package for gRPC logging middlewares.
5+
Package logging is a "parent" package for gRPC logging middlewares.
66
7-
The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call
8-
(e.g. service and method names).
7+
The gRPC logging middleware populates request-scoped data to `logging.Fields` that relate to the current gRPC call
8+
(e.g. service and method names). You can extract/inject data in the propagated context using `logging.ExtractFields` and `logging.InjectFields`.
99
10-
Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs
11-
that are made using the `ctx_logrus` or `ctx_zap` loggers.
10+
Once the gRPC logging middleware has added the gRPC specific Fields to the ctx they will then be written with the log lines.
1211
1312
All logging middleware will emit a final log statement. It is based on the error returned by the handler function,
14-
the gRPC status code, an error (if any) and it emit at a level controlled via `WithLevels`.
13+
the gRPC status code, an error (if any) and it emits at a level controlled via `WithLevels`. You can control this behavior
14+
using `WithDecider`.
1515
1616
This parent package
1717
1818
This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other
19-
logging middlewares *could* share . This allows code to be shared between different implementations.
19+
logging middlewares *could* share. This allows code to be shared between different implementations.
2020
2121
Field names
2222
@@ -31,6 +31,5 @@ Implementations:
3131
* providers/zerolog
3232
* providers/phuslog
3333
34-
See relevant packages below.
3534
*/
3635
package logging

interceptors/logging/interceptors.go

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,20 @@ import (
1010
"time"
1111

1212
"google.golang.org/grpc"
13+
"google.golang.org/grpc/peer"
1314

1415
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
15-
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
1616
)
1717

18-
// extractFields returns all fields from tags.
19-
func extractFields(tags tags.Tags) Fields {
20-
var fields Fields
21-
for k, v := range tags.Values() {
22-
fields = append(fields, k, v)
23-
}
24-
return fields
25-
}
26-
2718
type reporter struct {
19+
interceptors.CallMeta
20+
2821
ctx context.Context
29-
typ interceptors.GRPCType
30-
service, method string
31-
startCallLogged bool
32-
opts *options
33-
logger Logger
3422
kind string
23+
startCallLogged bool
24+
25+
opts *options
26+
logger Logger
3527
}
3628

3729
func (c *reporter) logMessage(logger Logger, err error, msg string, duration time.Duration) {
@@ -40,12 +32,11 @@ func (c *reporter) logMessage(logger Logger, err error, msg string, duration tim
4032
if err != nil {
4133
logger = logger.With("grpc.error", fmt.Sprintf("%v", err))
4234
}
43-
logger = logger.With(extractFields(tags.Extract(c.ctx))...)
4435
logger.With(c.opts.durationFieldFunc(duration)...).Log(c.opts.levelFunc(code), msg)
4536
}
4637

4738
func (c *reporter) PostCall(err error, duration time.Duration) {
48-
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method), err) {
39+
switch c.opts.shouldLog(c.FullMethod(), err) {
4940
case LogFinishCall, LogStartAndFinishCall:
5041
if err == io.EOF {
5142
err = nil
@@ -60,7 +51,7 @@ func (c *reporter) PostMsgSend(_ interface{}, err error, duration time.Duration)
6051
if c.startCallLogged {
6152
return
6253
}
63-
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method), err) {
54+
switch c.opts.shouldLog(c.FullMethod(), err) {
6455
case LogStartAndFinishCall:
6556
c.startCallLogged = true
6657
c.logMessage(c.logger, err, "started call", duration)
@@ -71,68 +62,71 @@ func (c *reporter) PostMsgReceive(_ interface{}, err error, duration time.Durati
7162
if c.startCallLogged {
7263
return
7364
}
74-
switch c.opts.shouldLog(interceptors.FullMethod(c.service, c.method), err) {
65+
switch c.opts.shouldLog(c.FullMethod(), err) {
7566
case LogStartAndFinishCall:
7667
c.startCallLogged = true
7768
c.logMessage(c.logger, err, "started call", duration)
7869
}
7970
}
8071

81-
type reportable struct {
82-
opts *options
83-
logger Logger
84-
}
85-
86-
func (r *reportable) ServerReporter(ctx context.Context, _ interface{}, typ interceptors.GRPCType, service string, method string) (interceptors.Reporter, context.Context) {
87-
return r.reporter(ctx, typ, service, method, KindServerFieldValue)
88-
}
72+
func reportable(logger Logger, opts *options) interceptors.CommonReportableFunc {
73+
return func(ctx context.Context, c interceptors.CallMeta, isClient bool) (interceptors.Reporter, context.Context) {
74+
kind := KindServerFieldValue
75+
if isClient {
76+
kind = KindClientFieldValue
77+
}
8978

90-
func (r *reportable) ClientReporter(ctx context.Context, _ interface{}, typ interceptors.GRPCType, service string, method string) (interceptors.Reporter, context.Context) {
91-
return r.reporter(ctx, typ, service, method, KindClientFieldValue)
92-
}
79+
fields := newCommonFields(kind, c)
80+
if !isClient {
81+
if peer, ok := peer.FromContext(ctx); ok {
82+
fields = append(fields, "peer.address", peer.Addr.String())
83+
}
84+
}
85+
fields = fields.AppendUnique(ExtractFields(ctx))
9386

94-
func (r *reportable) reporter(ctx context.Context, typ interceptors.GRPCType, service string, method string, kind string) (interceptors.Reporter, context.Context) {
95-
fields := commonFields(kind, typ, service, method)
96-
fields = append(fields, "grpc.start_time", time.Now().Format(r.opts.timestampFormat))
97-
if d, ok := ctx.Deadline(); ok {
98-
fields = append(fields, "grpc.request.deadline", d.Format(r.opts.timestampFormat))
87+
singleUseFields := []string{"grpc.start_time", time.Now().Format(opts.timestampFormat)}
88+
if d, ok := ctx.Deadline(); ok {
89+
singleUseFields = append(singleUseFields, "grpc.request.deadline", d.Format(opts.timestampFormat))
90+
}
91+
return &reporter{
92+
CallMeta: c,
93+
ctx: ctx,
94+
startCallLogged: false,
95+
opts: opts,
96+
logger: logger.With(fields...).With(singleUseFields...),
97+
kind: kind,
98+
}, InjectFields(ctx, fields)
9999
}
100-
return &reporter{
101-
ctx: ctx,
102-
typ: typ,
103-
service: service,
104-
method: method,
105-
startCallLogged: false,
106-
opts: r.opts,
107-
logger: r.logger.With(fields...),
108-
kind: kind,
109-
}, ctx
110100
}
111101

112102
// UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
113-
// Logger will use all tags (from tags package) available in current context as fields.
103+
// Logger will read existing and write new logging.Fields available in current context.
104+
// See `ExtractFields` and `InjectFields` for details.
114105
func UnaryClientInterceptor(logger Logger, opts ...Option) grpc.UnaryClientInterceptor {
115106
o := evaluateClientOpt(opts)
116-
return interceptors.UnaryClientInterceptor(&reportable{logger: logger, opts: o})
107+
return interceptors.UnaryClientInterceptor(reportable(logger, o))
117108
}
118109

119110
// StreamClientInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
120-
// Logger will use all tags (from tags package) available in current context as fields.
111+
// Logger will read existing and write new logging.Fields available in current context.
112+
// See `ExtractFields` and `InjectFields` for details.
121113
func StreamClientInterceptor(logger Logger, opts ...Option) grpc.StreamClientInterceptor {
122114
o := evaluateClientOpt(opts)
123-
return interceptors.StreamClientInterceptor(&reportable{logger: logger, opts: o})
115+
return interceptors.StreamClientInterceptor(reportable(logger, o))
124116
}
125117

126118
// UnaryServerInterceptor returns a new unary server interceptors that optionally logs endpoint handling.
127-
// Logger will use all tags (from tags package) available in current context as fields.
119+
// Logger will read existing and write new logging.Fields available in current context.
120+
// See `ExtractFields` and `InjectFields` for details.
128121
func UnaryServerInterceptor(logger Logger, opts ...Option) grpc.UnaryServerInterceptor {
129122
o := evaluateServerOpt(opts)
130-
return interceptors.UnaryServerInterceptor(&reportable{logger: logger, opts: o})
123+
return interceptors.UnaryServerInterceptor(reportable(logger, o))
131124
}
132125

133126
// StreamServerInterceptor returns a new stream server interceptors that optionally logs endpoint handling.
134-
// Logger will use all tags (from tags package) available in current context as fields.
127+
// Logger will read existing and write new logging.Fields available in current context.
128+
// See `ExtractFields` and `InjectFields` for details..
135129
func StreamServerInterceptor(logger Logger, opts ...Option) grpc.StreamServerInterceptor {
136130
o := evaluateServerOpt(opts)
137-
return interceptors.StreamServerInterceptor(&reportable{logger: logger, opts: o})
131+
return interceptors.StreamServerInterceptor(reportable(logger, o))
138132
}

0 commit comments

Comments
 (0)