github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-grpc-middleware/logging/zap/server_interceptors_test.go (about) 1 package grpc_zap_test 2 3 import ( 4 "io" 5 "runtime" 6 "strings" 7 "testing" 8 "time" 9 10 grpc_middleware "github.com/hxx258456/ccgo/go-grpc-middleware" 11 grpc_zap "github.com/hxx258456/ccgo/go-grpc-middleware/logging/zap" 12 grpc_ctxtags "github.com/hxx258456/ccgo/go-grpc-middleware/tags" 13 pb_testproto "github.com/hxx258456/ccgo/go-grpc-middleware/testing/testproto" 14 "github.com/hxx258456/ccgo/grpc" 15 "github.com/hxx258456/ccgo/grpc/codes" 16 "github.com/sirupsen/logrus" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 "github.com/stretchr/testify/suite" 20 "go.uber.org/zap" 21 "go.uber.org/zap/zapcore" 22 ) 23 24 func customCodeToLevel(c codes.Code) zapcore.Level { 25 if c == codes.Unauthenticated { 26 // Make this a special case for tests, and an error. 27 return zap.DPanicLevel 28 } 29 level := grpc_zap.DefaultCodeToLevel(c) 30 return level 31 } 32 33 func TestZapLoggingSuite(t *testing.T) { 34 if strings.HasPrefix(runtime.Version(), "go1.7") { 35 t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") 36 return 37 } 38 opts := []grpc_zap.Option{ 39 grpc_zap.WithLevels(customCodeToLevel), 40 } 41 b := newBaseZapSuite(t) 42 b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ 43 grpc_middleware.WithStreamServerChain( 44 grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), 45 grpc_zap.StreamServerInterceptor(b.log, opts...)), 46 grpc_middleware.WithUnaryServerChain( 47 grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), 48 grpc_zap.UnaryServerInterceptor(b.log, opts...)), 49 } 50 suite.Run(t, &zapServerSuite{b}) 51 } 52 53 type zapServerSuite struct { 54 *zapBaseSuite 55 } 56 57 func (s *zapServerSuite) TestPing_WithCustomTags() { 58 deadline := time.Now().Add(3 * time.Second) 59 _, err := s.Client.Ping(s.DeadlineCtx(deadline), goodPing) 60 require.NoError(s.T(), err, "there must be not be an error on a successful call") 61 62 msgs := s.getOutputJSONs() 63 require.Len(s.T(), msgs, 2, "two log statements should be logged") 64 for _, m := range msgs { 65 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 66 assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name") 67 assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)") 68 assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain `custom_tags.string`") 69 assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain fields extracted") 70 assert.Equal(s.T(), m["custom_field"], "custom_value", "all lines must contain `custom_field`") 71 72 assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int`") 73 require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time") 74 _, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string)) 75 assert.NoError(s.T(), err, "should be able to parse start time as RFC3339") 76 77 require.Contains(s.T(), m, "grpc.request.deadline", "all lines must contain the deadline of the call") 78 _, err = time.Parse(time.RFC3339, m["grpc.request.deadline"].(string)) 79 require.NoError(s.T(), err, "should be able to parse deadline as RFC3339") 80 assert.Equal(s.T(), m["grpc.request.deadline"], deadline.Format(time.RFC3339), "should have the same deadline that was set by the caller") 81 } 82 83 assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message") 84 85 assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "handler's message must contain user message") 86 assert.Equal(s.T(), msgs[1]["level"], "info", "must be logged at info level") 87 assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time") 88 } 89 90 func (s *zapServerSuite) TestPingError_WithCustomLevels() { 91 for _, tcase := range []struct { 92 code codes.Code 93 level zapcore.Level 94 msg string 95 }{ 96 { 97 code: codes.Internal, 98 level: zap.ErrorLevel, 99 msg: "Internal must remap to ErrorLevel in DefaultCodeToLevel", 100 }, 101 { 102 code: codes.NotFound, 103 level: zap.InfoLevel, 104 msg: "NotFound must remap to InfoLevel in DefaultCodeToLevel", 105 }, 106 { 107 code: codes.FailedPrecondition, 108 level: zap.WarnLevel, 109 msg: "FailedPrecondition must remap to WarnLevel in DefaultCodeToLevel", 110 }, 111 { 112 code: codes.Unauthenticated, 113 level: zap.DPanicLevel, 114 msg: "Unauthenticated is overwritten to DPanicLevel with customCodeToLevel override, which probably didn't work", 115 }, 116 } { 117 s.buffer.Reset() 118 _, err := s.Client.PingError( 119 s.SimpleCtx(), 120 &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(tcase.code)}) 121 require.Error(s.T(), err, "each call here must return an error") 122 123 msgs := s.getOutputJSONs() 124 require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr") 125 126 m := msgs[0] 127 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 128 assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name") 129 assert.Equal(s.T(), m["grpc.code"], tcase.code.String(), "all lines have the correct gRPC code") 130 assert.Equal(s.T(), m["level"], tcase.level.String(), tcase.msg) 131 assert.Equal(s.T(), m["msg"], "finished unary call with code "+tcase.code.String(), "needs the correct end message") 132 133 require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time") 134 _, err = time.Parse(time.RFC3339, m["grpc.start_time"].(string)) 135 assert.NoError(s.T(), err, "should be able to parse start time as RFC3339") 136 } 137 } 138 139 func (s *zapServerSuite) TestPingList_WithCustomTags() { 140 stream, err := s.Client.PingList(s.SimpleCtx(), goodPing) 141 require.NoError(s.T(), err, "should not fail on establishing the stream") 142 for { 143 _, err := stream.Recv() 144 if err == io.EOF { 145 break 146 } 147 require.NoError(s.T(), err, "reading stream should not fail") 148 } 149 msgs := s.getOutputJSONs() 150 require.Len(s.T(), msgs, 2, "two log statements should be logged") 151 152 for _, m := range msgs { 153 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 154 assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain method name") 155 assert.Equal(s.T(), m["span.kind"], "server", "all lines must contain the kind of call (server)") 156 assert.Equal(s.T(), m["custom_tags.string"], "something", "all lines must contain `custom_tags.string` set by AddFields") 157 assert.Equal(s.T(), m["grpc.request.value"], "something", "all lines must contain fields extracted from goodPing because of test.manual_extractfields.pb") 158 159 assert.Contains(s.T(), m, "custom_tags.int", "all lines must contain `custom_tags.int` set by AddFields") 160 require.Contains(s.T(), m, "grpc.start_time", "all lines must contain the start time") 161 _, err := time.Parse(time.RFC3339, m["grpc.start_time"].(string)) 162 assert.NoError(s.T(), err, "should be able to parse start time as RFC3339") 163 } 164 165 assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message") 166 167 assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "handler's message must contain user message") 168 assert.Equal(s.T(), msgs[1]["level"], "info", "OK codes must be logged on info level.") 169 assert.Contains(s.T(), msgs[1], "grpc.time_ms", "interceptor log statement should contain execution time") 170 } 171 172 func TestZapLoggingOverrideSuite(t *testing.T) { 173 if strings.HasPrefix(runtime.Version(), "go1.7") { 174 t.Skip("Skipping due to json.RawMessage incompatibility with go1.7") 175 return 176 } 177 opts := []grpc_zap.Option{ 178 grpc_zap.WithDurationField(grpc_zap.DurationToDurationField), 179 } 180 b := newBaseZapSuite(t) 181 b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ 182 grpc_middleware.WithStreamServerChain( 183 grpc_ctxtags.StreamServerInterceptor(), 184 grpc_zap.StreamServerInterceptor(b.log, opts...)), 185 grpc_middleware.WithUnaryServerChain( 186 grpc_ctxtags.UnaryServerInterceptor(), 187 grpc_zap.UnaryServerInterceptor(b.log, opts...)), 188 } 189 suite.Run(t, &zapServerOverrideSuite{b}) 190 } 191 192 type zapServerOverrideSuite struct { 193 *zapBaseSuite 194 } 195 196 func (s *zapServerOverrideSuite) TestPing_HasOverriddenDuration() { 197 _, err := s.Client.Ping(s.SimpleCtx(), goodPing) 198 require.NoError(s.T(), err, "there must be not be an error on a successful call") 199 msgs := s.getOutputJSONs() 200 require.Len(s.T(), msgs, 2, "two log statements should be logged") 201 202 for _, m := range msgs { 203 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 204 assert.Equal(s.T(), m["grpc.method"], "Ping", "all lines must contain method name") 205 } 206 assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message") 207 assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration") 208 assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration") 209 210 assert.Equal(s.T(), msgs[1]["msg"], "finished unary call with code OK", "handler's message must contain user message") 211 assert.Equal(s.T(), msgs[1]["level"], "info", "OK error codes must be logged on info level.") 212 assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "handler's message must not contain default duration") 213 assert.Contains(s.T(), msgs[1], "grpc.duration", "handler's message must contain overridden duration") 214 } 215 216 func (s *zapServerOverrideSuite) TestPingList_HasOverriddenDuration() { 217 stream, err := s.Client.PingList(s.SimpleCtx(), goodPing) 218 require.NoError(s.T(), err, "should not fail on establishing the stream") 219 for { 220 _, err := stream.Recv() 221 if err == io.EOF { 222 break 223 } 224 require.NoError(s.T(), err, "reading stream should not fail") 225 } 226 msgs := s.getOutputJSONs() 227 require.Len(s.T(), msgs, 2, "two log statements should be logged") 228 for _, m := range msgs { 229 s.T() 230 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 231 assert.Equal(s.T(), m["grpc.method"], "PingList", "all lines must contain method name") 232 } 233 234 assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message") 235 assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration") 236 assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration") 237 238 assert.Equal(s.T(), msgs[1]["msg"], "finished streaming call with code OK", "handler's message must contain user message") 239 assert.Equal(s.T(), msgs[1]["level"], "info", "OK error codes must be logged on info level.") 240 assert.NotContains(s.T(), msgs[1], "grpc.time_ms", "handler's message must not contain default duration") 241 assert.Contains(s.T(), msgs[1], "grpc.duration", "handler's message must contain overridden duration") 242 } 243 244 func TestZapServerOverrideSuppressedSuite(t *testing.T) { 245 if strings.HasPrefix(runtime.Version(), "go1.7") { 246 t.Skip("Skipping due to json.RawMessage incompatibility with go1.7") 247 return 248 } 249 opts := []grpc_zap.Option{ 250 grpc_zap.WithDecider(func(method string, err error) bool { 251 if err != nil && method == "/mwitkow.testproto.TestService/PingError" { 252 return true 253 } 254 return false 255 }), 256 } 257 b := newBaseZapSuite(t) 258 b.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ 259 grpc_middleware.WithStreamServerChain( 260 grpc_ctxtags.StreamServerInterceptor(), 261 grpc_zap.StreamServerInterceptor(b.log, opts...)), 262 grpc_middleware.WithUnaryServerChain( 263 grpc_ctxtags.UnaryServerInterceptor(), 264 grpc_zap.UnaryServerInterceptor(b.log, opts...)), 265 } 266 suite.Run(t, &zapServerOverridenDeciderSuite{b}) 267 } 268 269 type zapServerOverridenDeciderSuite struct { 270 *zapBaseSuite 271 } 272 273 func (s *zapServerOverridenDeciderSuite) TestPing_HasOverriddenDecider() { 274 _, err := s.Client.Ping(s.SimpleCtx(), goodPing) 275 require.NoError(s.T(), err, "there must be not be an error on a successful call") 276 msgs := s.getOutputJSONs() 277 require.Len(s.T(), msgs, 1, "single log statements should be logged") 278 279 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 280 assert.Equal(s.T(), msgs[0]["grpc.method"], "Ping", "all lines must contain method name") 281 assert.Equal(s.T(), msgs[0]["msg"], "some ping", "handler's message must contain user message") 282 } 283 284 func (s *zapServerOverridenDeciderSuite) TestPingError_HasOverriddenDecider() { 285 code := codes.NotFound 286 level := logrus.InfoLevel 287 msg := "NotFound must remap to InfoLevel in DefaultCodeToLevel" 288 289 s.buffer.Reset() 290 _, err := s.Client.PingError( 291 s.SimpleCtx(), 292 &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(code)}) 293 require.Error(s.T(), err, "each call here must return an error") 294 msgs := s.getOutputJSONs() 295 require.Len(s.T(), msgs, 1, "only the interceptor log message is printed in PingErr") 296 m := msgs[0] 297 assert.Equal(s.T(), m["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 298 assert.Equal(s.T(), m["grpc.method"], "PingError", "all lines must contain method name") 299 assert.Equal(s.T(), m["grpc.code"], code.String(), "all lines must contain the correct gRPC code") 300 assert.Equal(s.T(), m["level"], level.String(), msg) 301 } 302 303 func (s *zapServerOverridenDeciderSuite) TestPingList_HasOverriddenDecider() { 304 stream, err := s.Client.PingList(s.SimpleCtx(), goodPing) 305 require.NoError(s.T(), err, "should not fail on establishing the stream") 306 for { 307 _, err := stream.Recv() 308 if err == io.EOF { 309 break 310 } 311 require.NoError(s.T(), err, "reading stream should not fail") 312 } 313 msgs := s.getOutputJSONs() 314 require.Len(s.T(), msgs, 1, "single log statements should be logged") 315 316 assert.Equal(s.T(), msgs[0]["grpc.service"], "mwitkow.testproto.TestService", "all lines must contain service name") 317 assert.Equal(s.T(), msgs[0]["grpc.method"], "PingList", "all lines must contain method name") 318 assert.Equal(s.T(), msgs[0]["msg"], "some pinglist", "handler's message must contain user message") 319 320 assert.NotContains(s.T(), msgs[0], "grpc.time_ms", "handler's message must not contain default duration") 321 assert.NotContains(s.T(), msgs[0], "grpc.duration", "handler's message must not contain overridden duration") 322 }