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