github.com/m3db/m3@v1.5.0/src/x/grpc/grpc_metrics_test.go (about) 1 // Copyright (c) 2020 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package grpc 22 23 import ( 24 "context" 25 "fmt" 26 "io" 27 "net" 28 "testing" 29 "time" 30 31 testpb "github.com/m3db/m3/src/x/generated/proto/test" 32 33 "github.com/stretchr/testify/require" 34 "github.com/uber-go/tally" 35 "google.golang.org/grpc" 36 "google.golang.org/grpc/codes" 37 "google.golang.org/grpc/status" 38 ) 39 40 func TestInterceptors(t *testing.T) { 41 server, client, scope := testServerClient(t) 42 defer server.Close() 43 44 _, err := client.PingEmpty(context.Background(), &testpb.Empty{}) 45 require.NoError(t, err) 46 47 _, err = client.PingError(context.Background(), &testpb.PingRequest{ 48 ErrorCodeReturned: uint32(codes.FailedPrecondition), 49 }) 50 require.Error(t, err) 51 52 ss, err := client.PingList(context.Background(), &testpb.PingRequest{}) 53 require.NoError(t, err) 54 55 var recv int64 56 for { 57 _, err := ss.Recv() 58 if err == io.EOF { 59 break 60 } 61 require.NoError(t, err) 62 recv++ 63 } 64 65 expectedCounters := map[string]int64{ 66 "grpc_client_handled_total+grpc_code=FailedPrecondition,grpc_method=PingError,grpc_service=m3.test.TestService,grpc_type=unary": 1, 67 "grpc_client_handled_total+grpc_code=OK,grpc_method=PingEmpty,grpc_service=m3.test.TestService,grpc_type=unary": 1, 68 "grpc_client_handled_total+grpc_code=OK,grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": 1, 69 "grpc_client_msg_received_total+grpc_method=PingEmpty,grpc_service=m3.test.TestService,grpc_type=unary": 0, 70 "grpc_client_msg_received_total+grpc_method=PingError,grpc_service=m3.test.TestService,grpc_type=unary": 0, 71 "grpc_client_msg_received_total+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": recv, 72 "grpc_client_msg_sent_total+grpc_method=PingEmpty,grpc_service=m3.test.TestService,grpc_type=unary": 0, 73 "grpc_client_msg_sent_total+grpc_method=PingError,grpc_service=m3.test.TestService,grpc_type=unary": 0, 74 "grpc_client_msg_sent_total+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": 1, 75 "grpc_client_started_total+grpc_method=PingEmpty,grpc_service=m3.test.TestService,grpc_type=unary": 1, 76 "grpc_client_started_total+grpc_method=PingError,grpc_service=m3.test.TestService,grpc_type=unary": 1, 77 "grpc_client_started_total+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": 1, 78 } 79 80 snapshot := scope.Snapshot() 81 82 // Enable to see current values: 83 // for k, v := range snapshot.Counters() { 84 // fmt.Printf("\"%s\": %d,\n", k, v.Value()) 85 // } 86 // for k, v := range snapshot.Histograms() { 87 // fmt.Printf("%s = %v\n", k, v.Values()) 88 // } 89 90 for k, v := range expectedCounters { 91 counter, ok := snapshot.Counters()[k] 92 require.True(t, ok, fmt.Sprintf("metric missing: %s", k)) 93 require.Equal(t, v, counter.Value()) 94 } 95 96 expectedHistogramsTotalBucketCounts := map[string]int64{ 97 "grpc_client_handling_seconds+grpc_method=PingEmpty,grpc_service=m3.test.TestService,grpc_type=unary": 1, 98 "grpc_client_handling_seconds+grpc_method=PingError,grpc_service=m3.test.TestService,grpc_type=unary": 1, 99 "grpc_client_handling_seconds+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": 1, 100 "grpc_client_msg_recv_handling_seconds+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": recv + 1, 101 "grpc_client_msg_send_handling_seconds+grpc_method=PingList,grpc_service=m3.test.TestService,grpc_type=server_stream": 1, 102 } 103 for k, v := range expectedHistogramsTotalBucketCounts { 104 histogram, ok := snapshot.Histograms()[k] 105 require.True(t, ok, fmt.Sprintf("metric missing: %s", k)) 106 107 var actualBucketCount int64 108 for _, bucketCount := range histogram.Values() { 109 actualBucketCount += bucketCount 110 } 111 for _, bucketCount := range histogram.Durations() { 112 actualBucketCount += bucketCount 113 } 114 require.Equal(t, v, actualBucketCount, fmt.Sprintf("metric bucket count mismatch: %s", k)) 115 } 116 } 117 118 func testServerClient(t *testing.T) (net.Listener, testpb.TestServiceClient, tally.TestScope) { 119 serverListener, err := net.Listen("tcp", "127.0.0.1:0") 120 require.NoError(t, err) 121 122 server := grpc.NewServer() 123 testpb.RegisterTestServiceServer(server, testService{}) 124 125 go func() { 126 server.Serve(serverListener) 127 }() 128 129 s, _ := tally.NewRootScope(tally.ScopeOptions{Separator: "_"}, 0) 130 scope, ok := s.(tally.TestScope) 131 require.True(t, ok) 132 133 clientConn, err := grpc.Dial( 134 serverListener.Addr().String(), 135 grpc.WithInsecure(), 136 grpc.WithBlock(), 137 grpc.WithUnaryInterceptor(UnaryClientInterceptor(InterceptorInstrumentOptions{Scope: s})), 138 grpc.WithStreamInterceptor(StreamClientInterceptor(InterceptorInstrumentOptions{Scope: s})), 139 grpc.WithTimeout(2*time.Second)) 140 require.NoError(t, err) 141 return serverListener, testpb.NewTestServiceClient(clientConn), scope 142 } 143 144 type testService struct { 145 } 146 147 func (s testService) PingEmpty(ctx context.Context, _ *testpb.Empty) (*testpb.PingResponse, error) { 148 return &testpb.PingResponse{Value: "ping response", Counter: 42}, nil 149 } 150 151 func (s testService) Ping(ctx context.Context, ping *testpb.PingRequest) (*testpb.PingResponse, error) { 152 return &testpb.PingResponse{Value: ping.Value, Counter: 42}, nil 153 } 154 155 func (s testService) PingError(ctx context.Context, ping *testpb.PingRequest) (*testpb.Empty, error) { 156 code := codes.Code(ping.ErrorCodeReturned) 157 return nil, status.Errorf(code, "an error") 158 } 159 160 func (s testService) PingList(ping *testpb.PingRequest, stream testpb.TestService_PingListServer) error { 161 if ping.ErrorCodeReturned != 0 { 162 return status.Errorf(codes.Code(ping.ErrorCodeReturned), "an_error") 163 } 164 for i := 0; i < 20; i++ { 165 stream.Send(&testpb.PingResponse{Value: ping.Value, Counter: int32(i)}) 166 } 167 return nil 168 }