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  }