github.com/lzy4123/fabric@v2.1.1+incompatible/common/grpclogging/server_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package grpclogging_test
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"net"
    15  	"time"
    16  
    17  	"github.com/hyperledger/fabric/common/flogging"
    18  	"github.com/hyperledger/fabric/common/grpclogging"
    19  	"github.com/hyperledger/fabric/common/grpclogging/fakes"
    20  	"github.com/hyperledger/fabric/common/grpclogging/testpb"
    21  	. "github.com/onsi/ginkgo"
    22  	. "github.com/onsi/gomega"
    23  	"go.uber.org/zap"
    24  	"go.uber.org/zap/zapcore"
    25  	"go.uber.org/zap/zaptest/observer"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/credentials"
    29  	"google.golang.org/grpc/status"
    30  )
    31  
    32  const TimeThreshold = 100 * time.Millisecond
    33  
    34  var _ = Describe("Server", func() {
    35  	var (
    36  		fakeEchoService   *fakes.EchoServiceServer
    37  		echoServiceClient testpb.EchoServiceClient
    38  
    39  		listener        net.Listener
    40  		serveCompleteCh chan error
    41  		server          *grpc.Server
    42  		clientConn      *grpc.ClientConn
    43  
    44  		core     zapcore.Core
    45  		observed *observer.ObservedLogs
    46  		logger   *zap.Logger
    47  	)
    48  
    49  	BeforeEach(func() {
    50  		var err error
    51  		listener, err = net.Listen("tcp", "127.0.0.1:0")
    52  		Expect(err).NotTo(HaveOccurred())
    53  
    54  		core, observed = observer.New(zap.LevelEnablerFunc(func(zapcore.Level) bool { return true }))
    55  		logger = zap.New(core, zap.AddCaller()).Named("test-logger")
    56  
    57  		fakeEchoService = &fakes.EchoServiceServer{}
    58  		fakeEchoService.EchoStub = func(ctx context.Context, msg *testpb.Message) (*testpb.Message, error) {
    59  			msg.Sequence++
    60  			return msg, nil
    61  		}
    62  		fakeEchoService.EchoStreamStub = func(stream testpb.EchoService_EchoStreamServer) error {
    63  			msg, err := stream.Recv()
    64  			if err == io.EOF {
    65  				return nil
    66  			}
    67  			if err != nil {
    68  				return err
    69  			}
    70  
    71  			msg.Sequence++
    72  			return stream.Send(msg)
    73  		}
    74  
    75  		server = grpc.NewServer(
    76  			grpc.Creds(credentials.NewTLS(serverTLSConfig)),
    77  			grpc.StreamInterceptor(grpclogging.StreamServerInterceptor(logger)),
    78  			grpc.UnaryInterceptor(grpclogging.UnaryServerInterceptor(logger)),
    79  		)
    80  
    81  		testpb.RegisterEchoServiceServer(server, fakeEchoService)
    82  		serveCompleteCh = make(chan error, 1)
    83  		go func() { serveCompleteCh <- server.Serve(listener) }()
    84  
    85  		dialOpts := []grpc.DialOption{
    86  			grpc.WithTransportCredentials(credentials.NewTLS(clientTLSConfig)),
    87  			grpc.WithBlock(),
    88  		}
    89  		clientConn, err = grpc.Dial(listener.Addr().String(), dialOpts...)
    90  		Expect(err).NotTo(HaveOccurred())
    91  
    92  		echoServiceClient = testpb.NewEchoServiceClient(clientConn)
    93  	})
    94  
    95  	AfterEach(func() {
    96  		clientConn.Close()
    97  		server.Stop()
    98  
    99  		Eventually(serveCompleteCh).Should(Receive())
   100  	})
   101  
   102  	Describe("UnaryServerInterceptor", func() {
   103  		It("logs request data", func() {
   104  			ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   105  			defer cancel()
   106  
   107  			resp, err := echoServiceClient.Echo(ctx, &testpb.Message{Message: "hi"})
   108  			Expect(err).NotTo(HaveOccurred())
   109  			Expect(resp).To(Equal(&testpb.Message{Message: "hi", Sequence: 1}))
   110  
   111  			var logMessages []string
   112  			for _, entry := range observed.AllUntimed() {
   113  				logMessages = append(logMessages, entry.Message)
   114  			}
   115  			Expect(logMessages).To(ConsistOf(
   116  				"received unary request", // received payload
   117  				"sending unary response", // sending payload
   118  				"unary call completed",
   119  			))
   120  
   121  			for _, entry := range observed.AllUntimed() {
   122  				keyNames := map[string]struct{}{}
   123  				for _, field := range entry.Context {
   124  					keyNames[field.Key] = struct{}{}
   125  				}
   126  
   127  				switch entry.LoggerName {
   128  				case "test-logger":
   129  					Expect(entry.Level).To(Equal(zapcore.InfoLevel))
   130  					Expect(entry.Context).To(HaveLen(8))
   131  					Expect(keyNames).To(HaveLen(8))
   132  				case "test-logger.payload":
   133  					Expect(entry.Level).To(Equal(zapcore.DebugLevel - 1))
   134  					Expect(entry.Context).To(HaveLen(6))
   135  					Expect(keyNames).To(HaveLen(6))
   136  				default:
   137  					Fail("unexpected logger name: " + entry.LoggerName)
   138  				}
   139  				Expect(entry.Caller.String()).To(ContainSubstring("grpclogging/server.go"))
   140  
   141  				for _, field := range entry.Context {
   142  					switch field.Key {
   143  					case "grpc.code":
   144  						Expect(field.Type).To(Equal(zapcore.StringerType))
   145  						Expect(field.Interface).To(Equal(codes.OK))
   146  					case "grpc.call_duration":
   147  						Expect(field.Type).To(Equal(zapcore.DurationType))
   148  						Expect(field.Integer).NotTo(BeZero())
   149  					case "grpc.service":
   150  						Expect(field.Type).To(Equal(zapcore.StringType))
   151  						Expect(field.String).To(Equal("testpb.EchoService"))
   152  					case "grpc.method":
   153  						Expect(field.Type).To(Equal(zapcore.StringType))
   154  						Expect(field.String).To(Equal("Echo"))
   155  					case "grpc.request_deadline":
   156  						deadline, ok := ctx.Deadline()
   157  						Expect(ok).To(BeTrue())
   158  						Expect(field.Type).To(Equal(zapcore.TimeType))
   159  						Expect(field.Integer).NotTo(BeZero())
   160  						Expect(time.Unix(0, field.Integer)).To(BeTemporally("~", deadline, TimeThreshold))
   161  					case "grpc.peer_address":
   162  						Expect(field.Type).To(Equal(zapcore.StringType))
   163  						Expect(field.String).To(HavePrefix("127.0.0.1"))
   164  					case "grpc.peer_subject":
   165  						Expect(field.Type).To(Equal(zapcore.StringType))
   166  						Expect(field.String).To(HavePrefix("CN=client"))
   167  					case "message":
   168  						Expect(field.Type).To(Equal(zapcore.ReflectType))
   169  					case "error":
   170  						Expect(field.Type).To(Equal(zapcore.ErrorType))
   171  					case "":
   172  						Expect(field.Type).To(Equal(zapcore.SkipType))
   173  					default:
   174  						Fail("unexpected context field: " + field.Key)
   175  					}
   176  				}
   177  			}
   178  		})
   179  
   180  		It("provides a decorated context", func() {
   181  			ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   182  			defer cancel()
   183  			_, err := echoServiceClient.Echo(ctx, &testpb.Message{Message: "hi"})
   184  			Expect(err).NotTo(HaveOccurred())
   185  
   186  			Expect(fakeEchoService.EchoCallCount()).To(Equal(1))
   187  			echoContext, _ := fakeEchoService.EchoArgsForCall(0)
   188  			zapFields := grpclogging.ZapFields(echoContext)
   189  
   190  			keyNames := []string{}
   191  			for _, field := range zapFields {
   192  				keyNames = append(keyNames, field.Key)
   193  			}
   194  			Expect(keyNames).To(ConsistOf(
   195  				"grpc.service",
   196  				"grpc.method",
   197  				"grpc.request_deadline",
   198  				"grpc.peer_address",
   199  				"grpc.peer_subject",
   200  			))
   201  		})
   202  
   203  		Context("when the request ends with an unknown error", func() {
   204  			var expectedErr error
   205  
   206  			BeforeEach(func() {
   207  				expectedErr = errors.New("gah!")
   208  				fakeEchoService.EchoReturns(nil, expectedErr)
   209  
   210  				_, err := echoServiceClient.Echo(context.Background(), &testpb.Message{Message: "hi"})
   211  				Expect(err).To(HaveOccurred())
   212  			})
   213  
   214  			It("logs the unknown code", func() {
   215  				entries := observed.FilterMessage("unary call completed").FilterField(zap.Stringer("grpc.code", codes.Unknown)).AllUntimed()
   216  				Expect(entries).To(HaveLen(1))
   217  			})
   218  
   219  			It("logs the error", func() {
   220  				entries := observed.FilterMessage("unary call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
   221  				Expect(entries).To(HaveLen(1))
   222  			})
   223  		})
   224  
   225  		Context("when the request ends with a grpc status error", func() {
   226  			var expectedErr error
   227  
   228  			BeforeEach(func() {
   229  				expectedErr = &statusError{Status: status.New(codes.Aborted, "aborted")}
   230  				fakeEchoService.EchoReturns(nil, expectedErr)
   231  
   232  				_, err := echoServiceClient.Echo(context.Background(), &testpb.Message{Message: "hi"})
   233  				Expect(err).To(HaveOccurred())
   234  			})
   235  
   236  			It("logs the corect code", func() {
   237  				entries := observed.FilterMessage("unary call completed").FilterField(zap.Stringer("grpc.code", codes.Aborted)).AllUntimed()
   238  				Expect(entries).To(HaveLen(1))
   239  			})
   240  
   241  			It("logs the error", func() {
   242  				entries := observed.FilterMessage("unary call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
   243  				Expect(entries).To(HaveLen(1))
   244  			})
   245  		})
   246  
   247  		Context("when options are used", func() {
   248  			var (
   249  				listener        net.Listener
   250  				serveCompleteCh chan error
   251  				server          *grpc.Server
   252  				clientConn      *grpc.ClientConn
   253  
   254  				leveler        *fakes.Leveler
   255  				payloadLeveler *fakes.Leveler
   256  			)
   257  
   258  			BeforeEach(func() {
   259  				var err error
   260  				listener, err = net.Listen("tcp", "127.0.0.1:0")
   261  				Expect(err).NotTo(HaveOccurred())
   262  
   263  				leveler = &fakes.Leveler{}
   264  				leveler.Returns(zapcore.ErrorLevel)
   265  				payloadLeveler = &fakes.Leveler{}
   266  				payloadLeveler.Returns(zapcore.WarnLevel)
   267  
   268  				server = grpc.NewServer(
   269  					grpc.UnaryInterceptor(grpclogging.UnaryServerInterceptor(
   270  						logger,
   271  						grpclogging.WithLeveler(grpclogging.LevelerFunc(leveler.Spy)),
   272  						grpclogging.WithPayloadLeveler(grpclogging.LevelerFunc(payloadLeveler.Spy)),
   273  					)),
   274  				)
   275  
   276  				testpb.RegisterEchoServiceServer(server, fakeEchoService)
   277  				serveCompleteCh = make(chan error, 1)
   278  				go func() { serveCompleteCh <- server.Serve(listener) }()
   279  
   280  				clientConn, err = grpc.Dial(listener.Addr().String(), grpc.WithInsecure(), grpc.WithBlock())
   281  				Expect(err).NotTo(HaveOccurred())
   282  				echoServiceClient = testpb.NewEchoServiceClient(clientConn)
   283  
   284  				ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   285  				defer cancel()
   286  
   287  				_, err = echoServiceClient.Echo(ctx, &testpb.Message{Message: "hi"})
   288  				Expect(err).NotTo(HaveOccurred())
   289  			})
   290  
   291  			AfterEach(func() {
   292  				clientConn.Close()
   293  				server.Stop()
   294  
   295  				Eventually(serveCompleteCh).Should(Receive())
   296  			})
   297  
   298  			It("uses the levels returned by the levelers", func() {
   299  				Expect(leveler.CallCount()).To(Equal(1))
   300  				Expect(observed.FilterMessage("unary call completed").AllUntimed()[0].Level).To(Equal(zapcore.ErrorLevel))
   301  
   302  				Expect(payloadLeveler.CallCount()).To(Equal(1))
   303  				Expect(observed.FilterMessage("received unary request").AllUntimed()).To(HaveLen(1))
   304  				Expect(observed.FilterMessage("received unary request").AllUntimed()[0].Level).To(Equal(zapcore.WarnLevel))
   305  				Expect(observed.FilterMessage("sending unary response").AllUntimed()).To(HaveLen(1))
   306  				Expect(observed.FilterMessage("sending unary response").AllUntimed()[0].Level).To(Equal(zapcore.WarnLevel))
   307  			})
   308  
   309  			It("provides the decorated context and full method name to the levelers", func() {
   310  				Expect(leveler.CallCount()).To(Equal(1))
   311  				ctx, fullMethod := leveler.ArgsForCall(0)
   312  				Expect(grpclogging.ZapFields(ctx)).NotTo(BeEmpty())
   313  				Expect(fullMethod).To(Equal("/testpb.EchoService/Echo"))
   314  
   315  				Expect(payloadLeveler.CallCount()).To(Equal(1))
   316  				ctx, fullMethod = payloadLeveler.ArgsForCall(0)
   317  				Expect(grpclogging.ZapFields(ctx)).NotTo(BeEmpty())
   318  				Expect(fullMethod).To(Equal("/testpb.EchoService/Echo"))
   319  			})
   320  		})
   321  	})
   322  
   323  	Describe("StreamServerInterceptor", func() {
   324  		It("logs stream data", func() {
   325  			ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   326  			defer cancel()
   327  			streamClient, err := echoServiceClient.EchoStream(ctx)
   328  			Expect(err).NotTo(HaveOccurred())
   329  
   330  			err = streamClient.Send(&testpb.Message{Message: "hello"})
   331  			Expect(err).NotTo(HaveOccurred())
   332  
   333  			msg, err := streamClient.Recv()
   334  			Expect(err).NotTo(HaveOccurred())
   335  			Expect(msg).To(Equal(&testpb.Message{Message: "hello", Sequence: 1}))
   336  
   337  			err = streamClient.CloseSend()
   338  			Expect(err).NotTo(HaveOccurred())
   339  			_, err = streamClient.Recv()
   340  			Expect(err).To(Equal(io.EOF))
   341  
   342  			var logMessages []string
   343  			for _, entry := range observed.AllUntimed() {
   344  				logMessages = append(logMessages, entry.Message)
   345  			}
   346  			Expect(logMessages).To(ConsistOf(
   347  				"received stream message", // received payload
   348  				"sending stream message",  // sending payload
   349  				"streaming call completed",
   350  			))
   351  
   352  			for _, entry := range observed.AllUntimed() {
   353  				keyNames := map[string]struct{}{}
   354  				for _, field := range entry.Context {
   355  					keyNames[field.Key] = struct{}{}
   356  				}
   357  
   358  				switch entry.LoggerName {
   359  				case "test-logger":
   360  					Expect(entry.Level).To(Equal(zapcore.InfoLevel))
   361  					Expect(entry.Context).To(HaveLen(8))
   362  					Expect(keyNames).To(HaveLen(8))
   363  				case "test-logger.payload":
   364  					Expect(entry.Level).To(Equal(zapcore.DebugLevel - 1))
   365  					Expect(entry.Context).To(HaveLen(6))
   366  					Expect(keyNames).To(HaveLen(6))
   367  				default:
   368  					Fail("unexpected logger name: " + entry.LoggerName)
   369  				}
   370  				Expect(entry.Caller.String()).To(ContainSubstring("grpclogging/server.go"))
   371  
   372  				for _, field := range entry.Context {
   373  					switch field.Key {
   374  					case "grpc.code":
   375  						Expect(field.Type).To(Equal(zapcore.StringerType))
   376  						Expect(field.Interface).To(Equal(codes.OK))
   377  					case "grpc.call_duration":
   378  						Expect(field.Type).To(Equal(zapcore.DurationType))
   379  						Expect(field.Integer).NotTo(BeZero())
   380  					case "grpc.service":
   381  						Expect(field.Type).To(Equal(zapcore.StringType))
   382  						Expect(field.String).To(Equal("testpb.EchoService"))
   383  					case "grpc.method":
   384  						Expect(field.Type).To(Equal(zapcore.StringType))
   385  						Expect(field.String).To(Equal("EchoStream"))
   386  					case "grpc.request_deadline":
   387  						deadline, ok := ctx.Deadline()
   388  						Expect(ok).To(BeTrue())
   389  						Expect(field.Type).To(Equal(zapcore.TimeType))
   390  						Expect(field.Integer).NotTo(BeZero())
   391  						Expect(time.Unix(0, field.Integer)).To(BeTemporally("~", deadline, TimeThreshold))
   392  					case "grpc.peer_address":
   393  						Expect(field.Type).To(Equal(zapcore.StringType))
   394  						Expect(field.String).To(HavePrefix("127.0.0.1"))
   395  					case "grpc.peer_subject":
   396  						Expect(field.Type).To(Equal(zapcore.StringType))
   397  						Expect(field.String).To(HavePrefix("CN=client"))
   398  					case "message":
   399  						Expect(field.Type).To(Equal(zapcore.ReflectType))
   400  					case "error":
   401  						Expect(field.Type).To(Equal(zapcore.ErrorType))
   402  					case "":
   403  						Expect(field.Type).To(Equal(zapcore.SkipType))
   404  					default:
   405  						Fail("unexpected context field: " + field.Key)
   406  					}
   407  				}
   408  			}
   409  		})
   410  
   411  		It("provides a decorated context", func() {
   412  			ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   413  			defer cancel()
   414  			streamClient, err := echoServiceClient.EchoStream(ctx)
   415  			Expect(err).NotTo(HaveOccurred())
   416  
   417  			err = streamClient.Send(&testpb.Message{Message: "hello"})
   418  			Expect(err).NotTo(HaveOccurred())
   419  
   420  			msg, err := streamClient.Recv()
   421  			Expect(err).NotTo(HaveOccurred())
   422  			Expect(msg).To(Equal(&testpb.Message{Message: "hello", Sequence: 1}))
   423  
   424  			err = streamClient.CloseSend()
   425  			Expect(err).NotTo(HaveOccurred())
   426  			_, err = streamClient.Recv()
   427  			Expect(err).To(Equal(io.EOF))
   428  
   429  			Expect(fakeEchoService.EchoStreamCallCount()).To(Equal(1))
   430  			echoStream := fakeEchoService.EchoStreamArgsForCall(0)
   431  			zapFields := grpclogging.ZapFields(echoStream.Context())
   432  
   433  			keyNames := []string{}
   434  			for _, field := range zapFields {
   435  				keyNames = append(keyNames, field.Key)
   436  			}
   437  			Expect(keyNames).To(ConsistOf(
   438  				"grpc.service",
   439  				"grpc.method",
   440  				"grpc.request_deadline",
   441  				"grpc.peer_address",
   442  				"grpc.peer_subject",
   443  			))
   444  		})
   445  
   446  		Context("when tls client auth is missing", func() {
   447  			var clientConn *grpc.ClientConn
   448  
   449  			BeforeEach(func() {
   450  				dialOpts := []grpc.DialOption{
   451  					grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(caCertPool, "")),
   452  					grpc.WithBlock(),
   453  				}
   454  				var err error
   455  				clientConn, err = grpc.Dial(listener.Addr().String(), dialOpts...)
   456  				Expect(err).NotTo(HaveOccurred())
   457  
   458  				echoServiceClient = testpb.NewEchoServiceClient(clientConn)
   459  			})
   460  
   461  			AfterEach(func() {
   462  				clientConn.Close()
   463  			})
   464  
   465  			It("omits grpc.peer_subject", func() {
   466  				streamClient, err := echoServiceClient.EchoStream(context.Background())
   467  				Expect(err).NotTo(HaveOccurred())
   468  
   469  				err = streamClient.Send(&testpb.Message{Message: "hello"})
   470  				Expect(err).NotTo(HaveOccurred())
   471  
   472  				msg, err := streamClient.Recv()
   473  				Expect(err).NotTo(HaveOccurred())
   474  				Expect(msg).To(Equal(&testpb.Message{Message: "hello", Sequence: 1}))
   475  
   476  				err = streamClient.CloseSend()
   477  				Expect(err).NotTo(HaveOccurred())
   478  				_, err = streamClient.Recv()
   479  				Expect(err).To(Equal(io.EOF))
   480  
   481  				for _, entry := range observed.AllUntimed() {
   482  					keyNames := map[string]struct{}{}
   483  					for _, field := range entry.Context {
   484  						keyNames[field.Key] = struct{}{}
   485  					}
   486  					Expect(keyNames).NotTo(HaveKey("grpc.peer_subject"))
   487  				}
   488  			})
   489  		})
   490  
   491  		Context("when the stream ends with an unknown error", func() {
   492  			var expectedErr error
   493  
   494  			BeforeEach(func() {
   495  				expectedErr = errors.New("gah!")
   496  				fakeEchoService.EchoStreamStub = func(stream testpb.EchoService_EchoStreamServer) error {
   497  					stream.Recv()
   498  					return expectedErr
   499  				}
   500  
   501  				streamClient, err := echoServiceClient.EchoStream(context.Background())
   502  				Expect(err).NotTo(HaveOccurred())
   503  
   504  				err = streamClient.Send(&testpb.Message{Message: "hello"})
   505  				Expect(err).NotTo(HaveOccurred())
   506  				_, err = streamClient.Recv()
   507  				Expect(err).To(HaveOccurred())
   508  			})
   509  
   510  			It("logs the unknown code", func() {
   511  				entries := observed.FilterMessage("streaming call completed").FilterField(zap.Stringer("grpc.code", codes.Unknown)).AllUntimed()
   512  				Expect(entries).To(HaveLen(1))
   513  			})
   514  
   515  			It("logs the error", func() {
   516  				entries := observed.FilterMessage("streaming call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
   517  				Expect(entries).To(HaveLen(1))
   518  			})
   519  		})
   520  
   521  		Context("when the stream ends with a grpc status error", func() {
   522  			var expectedErr error
   523  
   524  			BeforeEach(func() {
   525  				errCh := make(chan error)
   526  				fakeEchoService.EchoStreamStub = func(svr testpb.EchoService_EchoStreamServer) error {
   527  					return <-errCh
   528  				}
   529  
   530  				streamClient, err := echoServiceClient.EchoStream(context.Background())
   531  				Expect(err).NotTo(HaveOccurred())
   532  
   533  				err = streamClient.Send(&testpb.Message{Message: "hello"})
   534  				Expect(err).NotTo(HaveOccurred())
   535  
   536  				expectedErr = &statusError{Status: status.New(codes.Aborted, "aborted")}
   537  				errCh <- expectedErr
   538  
   539  				_, err = streamClient.Recv()
   540  				Expect(err).To(HaveOccurred())
   541  			})
   542  
   543  			It("logs the corect code", func() {
   544  				entries := observed.FilterMessage("streaming call completed").FilterField(zap.Stringer("grpc.code", codes.Aborted)).AllUntimed()
   545  				Expect(entries).To(HaveLen(1))
   546  			})
   547  
   548  			It("logs the error", func() {
   549  				entries := observed.FilterMessage("streaming call completed").FilterField(grpclogging.Error(expectedErr)).AllUntimed()
   550  				Expect(entries).To(HaveLen(1))
   551  			})
   552  		})
   553  
   554  		Context("when options are used", func() {
   555  			var (
   556  				listener        net.Listener
   557  				serveCompleteCh chan error
   558  				server          *grpc.Server
   559  				clientConn      *grpc.ClientConn
   560  
   561  				leveler        *fakes.Leveler
   562  				payloadLeveler *fakes.Leveler
   563  			)
   564  
   565  			BeforeEach(func() {
   566  				var err error
   567  				listener, err = net.Listen("tcp", "127.0.0.1:0")
   568  				Expect(err).NotTo(HaveOccurred())
   569  
   570  				leveler = &fakes.Leveler{}
   571  				leveler.Returns(zapcore.ErrorLevel)
   572  				payloadLeveler = &fakes.Leveler{}
   573  				payloadLeveler.Returns(zapcore.WarnLevel)
   574  
   575  				server = grpc.NewServer(
   576  					grpc.StreamInterceptor(grpclogging.StreamServerInterceptor(
   577  						logger,
   578  						grpclogging.WithLeveler(grpclogging.LevelerFunc(leveler.Spy)),
   579  						grpclogging.WithPayloadLeveler(grpclogging.LevelerFunc(payloadLeveler.Spy)),
   580  					)),
   581  				)
   582  
   583  				testpb.RegisterEchoServiceServer(server, fakeEchoService)
   584  				serveCompleteCh = make(chan error, 1)
   585  				go func() { serveCompleteCh <- server.Serve(listener) }()
   586  
   587  				clientConn, err = grpc.Dial(listener.Addr().String(), grpc.WithInsecure(), grpc.WithBlock())
   588  				Expect(err).NotTo(HaveOccurred())
   589  				echoServiceClient = testpb.NewEchoServiceClient(clientConn)
   590  
   591  				streamClient, err := echoServiceClient.EchoStream(context.Background())
   592  				Expect(err).NotTo(HaveOccurred())
   593  				err = streamClient.Send(&testpb.Message{Message: "hello"})
   594  				Expect(err).NotTo(HaveOccurred())
   595  				msg, err := streamClient.Recv()
   596  				Expect(err).NotTo(HaveOccurred())
   597  				Expect(msg).To(Equal(&testpb.Message{Message: "hello", Sequence: 1}))
   598  
   599  				err = streamClient.CloseSend()
   600  				Expect(err).NotTo(HaveOccurred())
   601  				_, err = streamClient.Recv()
   602  				Expect(err).To(Equal(io.EOF))
   603  			})
   604  
   605  			AfterEach(func() {
   606  				clientConn.Close()
   607  
   608  				err := listener.Close()
   609  				Expect(err).NotTo(HaveOccurred())
   610  				Eventually(serveCompleteCh).Should(Receive())
   611  			})
   612  
   613  			It("uses the levels returned by the levelers", func() {
   614  				Expect(leveler.CallCount()).To(Equal(1))
   615  				Expect(observed.FilterMessage("streaming call completed").AllUntimed()[0].Level).To(Equal(zapcore.ErrorLevel))
   616  
   617  				Expect(payloadLeveler.CallCount()).To(Equal(1))
   618  				Expect(observed.FilterMessage("received stream message").AllUntimed()).To(HaveLen(1))
   619  				Expect(observed.FilterMessage("received stream message").AllUntimed()[0].Level).To(Equal(zapcore.WarnLevel))
   620  				Expect(observed.FilterMessage("sending stream message").AllUntimed()).To(HaveLen(1))
   621  				Expect(observed.FilterMessage("sending stream message").AllUntimed()[0].Level).To(Equal(zapcore.WarnLevel))
   622  			})
   623  
   624  			It("provides the decorated context and full method name to the levelers", func() {
   625  				Expect(leveler.CallCount()).To(Equal(1))
   626  				ctx, fullMethod := leveler.ArgsForCall(0)
   627  				Expect(grpclogging.ZapFields(ctx)).NotTo(BeEmpty())
   628  				Expect(fullMethod).To(Equal("/testpb.EchoService/EchoStream"))
   629  
   630  				Expect(payloadLeveler.CallCount()).To(Equal(1))
   631  				ctx, fullMethod = payloadLeveler.ArgsForCall(0)
   632  				Expect(grpclogging.ZapFields(ctx)).NotTo(BeEmpty())
   633  				Expect(fullMethod).To(Equal("/testpb.EchoService/EchoStream"))
   634  			})
   635  		})
   636  	})
   637  
   638  	It("uses flogging.PayloadLevel as DefaultPayloadLevel", func() {
   639  		Expect(grpclogging.DefaultPayloadLevel).To(Equal(flogging.PayloadLevel))
   640  	})
   641  })
   642  
   643  type statusError struct{ *status.Status }
   644  
   645  func (s *statusError) GRPCStatus() *status.Status { return s.Status }
   646  
   647  func (s *statusError) Error() string {
   648  	return fmt.Sprintf("🎶 I'm a little error, short and sweet. Here is my message: %s. Here is my code: %d.🎶", s.Status.Message(), s.Status.Code())
   649  }