1 package grpc_zap_test
2
3 import (
4 "io"
5 "testing"
6
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/require"
9 "github.com/stretchr/testify/suite"
10 "google.golang.org/grpc"
11 "google.golang.org/grpc/codes"
12
13 grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
14 pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
15 "go.uber.org/zap/zapcore"
16 )
17
18 func customClientCodeToLevel(c codes.Code) zapcore.Level {
19 if c == codes.Unauthenticated {
20
21 return zapcore.ErrorLevel
22 }
23 level := grpc_zap.DefaultClientCodeToLevel(c)
24 return level
25 }
26
27 func TestZapClientSuite(t *testing.T) {
28 opts := []grpc_zap.Option{
29 grpc_zap.WithLevels(customClientCodeToLevel),
30 }
31 b := newBaseZapSuite(t)
32 b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
33 grpc.WithUnaryInterceptor(grpc_zap.UnaryClientInterceptor(b.log, opts...)),
34 grpc.WithStreamInterceptor(grpc_zap.StreamClientInterceptor(b.log, opts...)),
35 }
36 suite.Run(t, &zapClientSuite{b})
37 }
38
39 type zapClientSuite struct {
40 *zapBaseSuite
41 }
42
43 func (s *zapClientSuite) TestPing() {
44 _, err := s.Client.Ping(s.SimpleCtx(), goodPing)
45 require.NoError(s.T(), err, "there must be not be an error on a successful call")
46
47 msgs := s.getOutputJSONs()
48 require.Len(s.T(), msgs, 1, "one log statement should be logged")
49
50 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
51 assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
52 assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "must contain correct message")
53 assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
54 assert.Equal(s.T(), msgs[0]["level"], "debug", "must be logged on debug level.")
55 assert.Contains(s.T(), msgs[0], "grpc.time_ms", "interceptor log statement should contain execution time")
56 }
57
58 func (s *zapClientSuite) TestPingList() {
59 stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
60 require.NoError(s.T(), err, "should not fail on establishing the stream")
61 for {
62 _, err := stream.Recv()
63 if err == io.EOF {
64 break
65 }
66 require.NoError(s.T(), err, "reading stream should not fail")
67 }
68 msgs := s.getOutputJSONs()
69 require.Len(s.T(), msgs, 1, "one log statement should be logged")
70
71 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
72 assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
73 assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "handler's message must contain user message")
74 assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
75 assert.Equal(s.T(), msgs[0]["level"], "debug", "OK codes must be logged on debug level.")
76 assert.Contains(s.T(), msgs[0], "grpc.time_ms", "handler's message must contain time in ms")
77 }
78
79 func (s *zapClientSuite) TestPingError_WithCustomLevels() {
80 for _, tcase := range []struct {
81 code codes.Code
82 level zapcore.Level
83 msg string
84 }{
85 {
86 code: codes.Internal,
87 level: zapcore.WarnLevel,
88 msg: "Internal must remap to ErrorLevel in DefaultClientCodeToLevel",
89 },
90 {
91 code: codes.NotFound,
92 level: zapcore.DebugLevel,
93 msg: "NotFound must remap to InfoLevel in DefaultClientCodeToLevel",
94 },
95 {
96 code: codes.FailedPrecondition,
97 level: zapcore.DebugLevel,
98 msg: "FailedPrecondition must remap to WarnLevel in DefaultClientCodeToLevel",
99 },
100 {
101 code: codes.Unauthenticated,
102 level: zapcore.ErrorLevel,
103 msg: "Unauthenticated is overwritten to ErrorLevel with customClientCodeToLevel override, which probably didn't work",
104 },
105 } {
106 s.SetupTest()
107 _, err := s.Client.PingError(
108 s.SimpleCtx(),
109 &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)})
110 require.Error(s.T(), err, "each call here must return an error")
111
112 msgs := s.getOutputJSONs()
113 require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr")
114
115 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
116 assert.Equal(s.T(), msgs[0]["grpc.method"], "PingError", "all lines must contain method name")
117 assert.Equal(s.T(), msgs[0]["grpc.code"], tcase.code.String(), "all lines must contain the correct gRPC code")
118 assert.Equal(s.T(), msgs[0]["level"], tcase.level.String(), tcase.msg)
119 }
120 }
121
122 func TestZapClientOverrideSuite(t *testing.T) {
123 opts := []grpc_zap.Option{
124 grpc_zap.WithDurationField(grpc_zap.DurationToDurationField),
125 }
126 b := newBaseZapSuite(t)
127 b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
128 grpc.WithUnaryInterceptor(grpc_zap.UnaryClientInterceptor(b.log, opts...)),
129 grpc.WithStreamInterceptor(grpc_zap.StreamClientInterceptor(b.log, opts...)),
130 }
131 suite.Run(t, &zapClientOverrideSuite{b})
132 }
133
134 type zapClientOverrideSuite struct {
135 *zapBaseSuite
136 }
137
138 func (s *zapClientOverrideSuite) TestPing_HasOverrides() {
139 _, err := s.Client.Ping(s.SimpleCtx(), goodPing)
140 require.NoError(s.T(), err, "there must be not be an error on a successful call")
141
142 msgs := s.getOutputJSONs()
143 require.Len(s.T(), msgs, 1, "one log statement should be logged")
144
145 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
146 assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
147 assert.Equal(s.T(), msgs[0]["msg"], "finished client unary call", "handler's message must contain user message")
148
149 assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
150 assert.Contains(s.T(), msgs[0], "grpc.duration", "handler's message must contain overridden duration")
151 }
152
153 func (s *zapClientOverrideSuite) TestPingList_HasOverrides() {
154 stream, err := s.Client.PingList(s.SimpleCtx(), goodPing)
155 require.NoError(s.T(), err, "should not fail on establishing the stream")
156 for {
157 _, err := stream.Recv()
158 if err == io.EOF {
159 break
160 }
161 require.NoError(s.T(), err, "reading stream should not fail")
162 }
163 msgs := s.getOutputJSONs()
164 require.Len(s.T(), msgs, 1, "one log statement should be logged")
165
166 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
167 assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name")
168 assert.Equal(s.T(), msgs[0]["msg"], "finished client streaming call", "handler's message must contain user message")
169 assert.Equal(s.T(), msgs[0]["span.kind"], "client", "all lines must contain the kind of call (client)")
170 assert.Equal(s.T(), msgs[0]["level"], "debug", "must be logged on debug level.")
171
172 assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration")
173 assert.Contains(s.T(), msgs[0], "grpc.duration", "handler's message must contain overridden duration")
174 }
175
176 func TestZapLoggingClientMessageProducerSuite(t *testing.T) {
177 opts := []grpc_zap.Option{
178 grpc_zap.WithMessageProducer(StubMessageProducer),
179 }
180 b := newBaseZapSuite(t)
181 b.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
182 grpc.WithUnaryInterceptor(grpc_zap.UnaryClientInterceptor(b.log, opts...)),
183 grpc.WithStreamInterceptor(grpc_zap.StreamClientInterceptor(b.log, opts...)),
184 }
185 suite.Run(t, &zapClientMessageProducerSuite{b})
186 }
187
188 type zapClientMessageProducerSuite struct {
189 *zapBaseSuite
190 }
191
192 func (s *zapClientMessageProducerSuite) TestPing_HasOverriddenMessageProducer() {
193 _, err := s.Client.Ping(s.SimpleCtx(), goodPing)
194 require.NoError(s.T(), err, "there must be not be an error on a successful call")
195
196 msgs := s.getOutputJSONs()
197 require.Len(s.T(), msgs, 1, "one log statement should be logged")
198
199 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name")
200 assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name")
201 assert.Equal(s.T(), msgs[0]["msg"], "custom message", "handler's message must contain user message")
202 }
203
View as plain text