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