Skip to content

Commit 2f02a32

Browse files
committed
Prevent verbose error reporting from grpc logging
By default, for errors that implement fmt.Formatter, zap will add a verboseError field that includes the '%+v' formatted error in the log record. This is really cool and useful but it can be noisy. In order to prevent this noise in the grpc interceptors, we can embed the errors we're logging in a struct; the result is that the struct satisfies the error interface without implementing fmt.Formatter. FAB-12953 #done Change-Id: I312a70a0bc0c0ac88765de319ba67c8ee8c1e3cd Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
1 parent 6922e14 commit 2f02a32

File tree

4 files changed

+93
-51
lines changed

4 files changed

+93
-51
lines changed

common/grpclogging/fields.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,14 @@ func ProtoMessage(key string, val interface{}) zapcore.Field {
3232
}
3333
return zap.Any(key, val)
3434
}
35+
36+
func Error(err error) zapcore.Field {
37+
if err == nil {
38+
return zap.Skip()
39+
}
40+
41+
// Wrap the error so it no longer implements fmt.Formatter. This will prevent
42+
// zap from adding the "verboseError" field to the log record that includes a
43+
// full stack trace.
44+
return zap.Error(struct{ error }{err})
45+
}

common/grpclogging/fields_test.go

Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,74 +8,105 @@ package grpclogging_test
88

99
import (
1010
"encoding/json"
11-
"errors"
1211

1312
"github.com/golang/protobuf/jsonpb"
1413
"github.com/hyperledger/fabric/common/grpclogging"
1514
"github.com/hyperledger/fabric/common/grpclogging/testpb"
1615
. "github.com/onsi/ginkgo"
1716
. "github.com/onsi/gomega"
17+
"github.com/pkg/errors"
1818
"go.uber.org/zap"
1919
"go.uber.org/zap/zapcore"
2020
)
2121

22-
var _ = Describe("ProtoMessage", func() {
23-
var message *testpb.Message
22+
var _ = Describe("Fields", func() {
23+
Describe("ProtoMessage", func() {
24+
var message *testpb.Message
2425

25-
BeforeEach(func() {
26-
message = &testpb.Message{
27-
Message: "Je suis une pizza avec du fromage.",
28-
Sequence: 1337,
29-
}
30-
})
26+
BeforeEach(func() {
27+
message = &testpb.Message{
28+
Message: "Je suis une pizza avec du fromage.",
29+
Sequence: 1337,
30+
}
31+
})
3132

32-
It("creates a reflect field for zap", func() {
33-
field := grpclogging.ProtoMessage("field-key", message)
34-
Expect(field.Key).To(Equal("field-key"))
35-
_, ok := field.Interface.(json.Marshaler)
36-
Expect(ok).To(BeTrue())
37-
})
33+
It("creates a reflect field for zap", func() {
34+
field := grpclogging.ProtoMessage("field-key", message)
35+
Expect(field.Key).To(Equal("field-key"))
36+
_, ok := field.Interface.(json.Marshaler)
37+
Expect(ok).To(BeTrue())
38+
})
3839

39-
It("marshals messages compatible with jsonpb", func() {
40-
field := grpclogging.ProtoMessage("field-key", message)
41-
marshaler := field.Interface.(json.Marshaler)
40+
It("marshals messages compatible with jsonpb", func() {
41+
field := grpclogging.ProtoMessage("field-key", message)
42+
marshaler := field.Interface.(json.Marshaler)
4243

43-
marshaled, err := marshaler.MarshalJSON()
44-
Expect(err).NotTo(HaveOccurred())
44+
marshaled, err := marshaler.MarshalJSON()
45+
Expect(err).NotTo(HaveOccurred())
4546

46-
protoMarshaler := &jsonpb.Marshaler{}
47-
protoJson, err := protoMarshaler.MarshalToString(message)
48-
Expect(err).NotTo(HaveOccurred())
47+
protoMarshaler := &jsonpb.Marshaler{}
48+
protoJson, err := protoMarshaler.MarshalToString(message)
49+
Expect(err).NotTo(HaveOccurred())
4950

50-
Expect(marshaled).To(MatchJSON(protoJson))
51-
})
51+
Expect(marshaled).To(MatchJSON(protoJson))
52+
})
5253

53-
It("works with zap's json encoder", func() {
54-
encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
55-
MessageKey: "message",
54+
It("works with zap's json encoder", func() {
55+
encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
56+
MessageKey: "message",
57+
})
58+
buf, err := encoder.EncodeEntry(
59+
zapcore.Entry{Message: "Oh là là"},
60+
[]zapcore.Field{grpclogging.ProtoMessage("proto-message", message)},
61+
)
62+
Expect(err).NotTo(HaveOccurred())
63+
Expect(buf.String()).To(MatchJSON(`{"message": "Oh là là", "proto-message": {"message": "Je suis une pizza avec du fromage.", "sequence": 1337}}`))
5664
})
57-
buf, err := encoder.EncodeEntry(
58-
zapcore.Entry{Message: "Oh là là"},
59-
[]zapcore.Field{grpclogging.ProtoMessage("proto-message", message)},
60-
)
61-
Expect(err).NotTo(HaveOccurred())
62-
Expect(buf.String()).To(MatchJSON(`{"message": "Oh là là", "proto-message": {"message": "Je suis une pizza avec du fromage.", "sequence": 1337}}`))
63-
})
6465

65-
Context("when marshaling the message fails", func() {
66-
It("it returns the error from marshaling", func() {
67-
field := grpclogging.ProtoMessage("field-key", badProto{err: errors.New("Boom!")})
68-
marshaler := field.Interface.(json.Marshaler)
66+
Context("when marshaling the message fails", func() {
67+
It("it returns the error from marshaling", func() {
68+
field := grpclogging.ProtoMessage("field-key", badProto{err: errors.New("Boom!")})
69+
marshaler := field.Interface.(json.Marshaler)
70+
71+
_, err := marshaler.MarshalJSON()
72+
Expect(err).To(MatchError("Boom!"))
73+
})
74+
})
6975

70-
_, err := marshaler.MarshalJSON()
71-
Expect(err).To(MatchError("Boom!"))
76+
Context("when something other than a proto.Message is provided", func() {
77+
It("creates an any field", func() {
78+
field := grpclogging.ProtoMessage("field-key", "Je ne suis pas une pizza.")
79+
Expect(field).To(Equal(zap.Any("field-key", "Je ne suis pas une pizza.")))
80+
})
7281
})
7382
})
7483

75-
Context("when something other than a proto.Message is provided", func() {
76-
It("creates an any field", func() {
77-
field := grpclogging.ProtoMessage("field-key", "Je ne suis pas une pizza.")
78-
Expect(field).To(Equal(zap.Any("field-key", "Je ne suis pas une pizza.")))
84+
Describe("Error", func() {
85+
It("creates an error field for zap", func() {
86+
err := errors.New("error")
87+
field := grpclogging.Error(err)
88+
Expect(field.Key).To(Equal("error"))
89+
Expect(field.Type).To(Equal(zapcore.ErrorType))
90+
Expect(field.Interface).To(Equal(struct{ error }{err}))
91+
})
92+
93+
Context("when the error is nil", func() {
94+
It("creates a skip field", func() {
95+
field := grpclogging.Error(nil)
96+
Expect(field.Type).To(Equal(zapcore.SkipType))
97+
})
98+
})
99+
100+
It("omits the verboseError field", func() {
101+
encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
102+
MessageKey: "message",
103+
})
104+
buf, err := encoder.EncodeEntry(
105+
zapcore.Entry{Message: "the message"},
106+
[]zapcore.Field{grpclogging.Error(errors.New("the error"))},
107+
)
108+
Expect(err).NotTo(HaveOccurred())
109+
Expect(buf.String()).To(MatchJSON(`{"message": "the message", "error": "the error"}`))
79110
})
80111
})
81112
})

common/grpclogging/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func UnaryServerInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryServer
9696

9797
if ce := logger.Check(o.Level(ctx, info.FullMethod), "unary call completed"); ce != nil {
9898
ce.Write(
99-
zap.Error(err),
99+
Error(err),
100100
zap.Stringer("grpc.code", grpc.Code(err)),
101101
zap.Duration("grpc.call_duration", time.Since(startTime)),
102102
)
@@ -128,7 +128,7 @@ func StreamServerInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamServ
128128
err := handler(service, wrappedStream)
129129
if ce := logger.Check(o.Level(ctx, info.FullMethod), "streaming call completed"); ce != nil {
130130
ce.Write(
131-
zap.Error(err),
131+
Error(err),
132132
zap.Stringer("grpc.code", grpc.Code(err)),
133133
zap.Duration("grpc.call_duration", time.Since(startTime)),
134134
)

common/grpclogging/server_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ var _ = Describe("Server", func() {
255255
})
256256

257257
It("logs the error", func() {
258-
entries := observed.FilterMessage("unary call completed").FilterField(zap.Error(expectedErr)).AllUntimed()
258+
entries := observed.FilterMessage("unary call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
259259
Expect(entries).To(HaveLen(1))
260260
})
261261
})
@@ -277,7 +277,7 @@ var _ = Describe("Server", func() {
277277
})
278278

279279
It("logs the error", func() {
280-
entries := observed.FilterMessage("unary call completed").FilterField(zap.Error(expectedErr)).AllUntimed()
280+
entries := observed.FilterMessage("unary call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
281281
Expect(entries).To(HaveLen(1))
282282
})
283283
})
@@ -551,7 +551,7 @@ var _ = Describe("Server", func() {
551551
})
552552

553553
It("logs the error", func() {
554-
entries := observed.FilterMessage("streaming call completed").FilterField(zap.Error(expectedErr)).AllUntimed()
554+
entries := observed.FilterMessage("streaming call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
555555
Expect(entries).To(HaveLen(1))
556556
})
557557
})
@@ -578,7 +578,7 @@ var _ = Describe("Server", func() {
578578
})
579579

580580
It("logs the error", func() {
581-
entries := observed.FilterMessage("streaming call completed").FilterField(zap.Error(expectedErr)).AllUntimed()
581+
entries := observed.FilterMessage("streaming call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
582582
Expect(entries).To(HaveLen(1))
583583
})
584584
})

0 commit comments

Comments
 (0)