go.uber.org/yarpc@v1.72.1/internal/observability/graph_test.go (about) 1 // Copyright (c) 2022 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 observability 22 23 import ( 24 "context" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 "go.uber.org/net/metrics" 29 "go.uber.org/yarpc/api/transport" 30 "go.uber.org/yarpc/api/transport/transporttest" 31 "go.uber.org/zap" 32 "go.uber.org/zap/zaptest/observer" 33 ) 34 35 func TestHandleWithReservedField(t *testing.T) { 36 root := metrics.New() 37 meter := root.Scope() 38 39 loggerCore, loggerObs := observer.New(zap.ErrorLevel) 40 logger := zap.New(loggerCore) 41 42 mw := NewMiddleware(Config{Scope: meter, Logger: logger, MetricTagsBlocklist: []string{"routing_delegate"}}) 43 44 for _, rd := range []string{"rd1", "rd2"} { 45 req := &transport.Request{ 46 Caller: "caller", 47 Service: "service", 48 Transport: "", 49 Encoding: "raw", 50 Procedure: "procedure", 51 ShardKey: "sk", 52 RoutingDelegate: rd, 53 } 54 // if "routing_delegate" is part of metricTagsBlockMap 55 // multiple requests with all other same field value but RoutingDelegate 56 // should successfully create a single metrics edge, without any error logging. 57 assert.NoError(t, mw.Handle(context.Background(), req, &transporttest.FakeResponseWriter{}, &fakeHandler{})) 58 assert.Len(t, loggerObs.All(), 0) 59 } 60 } 61 62 func TestMetricsTagIgnore(t *testing.T) { 63 req := &transport.Request{ 64 Caller: "caller", 65 Service: "service", 66 Transport: "", 67 Encoding: "proto", 68 Procedure: "procedure", 69 RoutingKey: "rk", 70 RoutingDelegate: "rd", 71 } 72 73 tests := []struct { 74 desc string 75 metricsToIgnore []string 76 expected *metricsTagIgnore 77 expectedTags metrics.Tags 78 }{ 79 { 80 desc: "empty ignore list", 81 expected: &metricsTagIgnore{}, // all fields default to false 82 expectedTags: metrics.Tags{ 83 _source: "caller", 84 _dest: "service", 85 _transport: "unknown", 86 _procedure: "procedure", 87 _encoding: "proto", 88 _routingKey: "rk", 89 _routingDelegate: "rd", 90 _direction: "inbound", 91 _rpcType: "Unary", 92 }, 93 }, 94 { 95 desc: "only reserved fields in ignore list", 96 metricsToIgnore: []string{_source, _dest, _transport, _procedure, _encoding, _routingKey, _routingDelegate, _direction, _rpcType}, 97 expected: &metricsTagIgnore{ 98 source: true, 99 dest: true, 100 transport: true, 101 procedure: true, 102 encoding: true, 103 routingKey: true, 104 routingDelegate: true, 105 direction: true, 106 rpcType: true, 107 }, 108 expectedTags: metrics.Tags{ 109 _source: "__dropped__", 110 _dest: "__dropped__", 111 _transport: "__dropped__", 112 _procedure: "__dropped__", 113 _encoding: "__dropped__", 114 _routingKey: "__dropped__", 115 _routingDelegate: "__dropped__", 116 _direction: "__dropped__", 117 _rpcType: "__dropped__", 118 }, 119 }, 120 { 121 desc: "reserved fields and other fields in ignore list", 122 metricsToIgnore: []string{_source, _transport, _rpcType, "user_defined1", "user_defined2"}, 123 expected: &metricsTagIgnore{ 124 source: true, 125 transport: true, 126 rpcType: true, 127 }, 128 expectedTags: metrics.Tags{ 129 _source: "__dropped__", 130 _dest: "service", 131 _transport: "__dropped__", 132 _procedure: "procedure", 133 _encoding: "proto", 134 _routingKey: "rk", 135 _routingDelegate: "rd", 136 _direction: "inbound", 137 _rpcType: "__dropped__", 138 }, 139 }, 140 } 141 for _, tt := range tests { 142 t.Run(tt.desc, func(t *testing.T) { 143 actual := newMetricsTagIgnore(tt.metricsToIgnore) 144 assert.Equal(t, tt.expected, actual, "wrong metricsToIgnore") 145 actualTags := actual.tags(req, "inbound", transport.Unary) 146 assert.Equal(t, tt.expectedTags, actualTags, "tags mismatch") 147 }) 148 } 149 } 150 151 func TestEdgeNopFallbacks(t *testing.T) { 152 // If we fail to create any of the metrics required for the edge, we should 153 // fall back to no-op implementations. The easiest way to trigger failures 154 // is to re-use the same Registry. 155 root := metrics.New() 156 meter := root.Scope() 157 req := &transport.Request{ 158 Caller: "caller", 159 Service: "service", 160 Transport: "", 161 Encoding: "raw", 162 Procedure: "procedure", 163 ShardKey: "sk", 164 RoutingKey: "rk", 165 RoutingDelegate: "rd", 166 } 167 168 // Should succeed, covered by middleware tests. 169 _ = newEdge(zap.NewNop(), meter, &metricsTagIgnore{}, req, string(_directionOutbound), transport.Unary) 170 171 // Should fall back to no-op metrics. 172 // Usage of nil metrics should not panic, should not observe changes. 173 e := newEdge(zap.NewNop(), meter, &metricsTagIgnore{}, req, string(_directionOutbound), transport.Unary) 174 175 e.calls.Inc() 176 assert.Equal(t, int64(0), e.calls.Load(), "Expected to fall back to no-op metrics.") 177 178 e.successes.Load() 179 assert.Equal(t, int64(0), e.successes.Load(), "Expected to fall back to no-op metrics.") 180 181 cf, err := e.callerFailures.Get() 182 assert.NoError(t, err, "Unexpected error getting caller failure counter") 183 cf.Inc() 184 assert.Equal(t, int64(0), cf.Load(), "Expected to fall back to no-op metrics.") 185 186 sf, err := e.serverFailures.Get() 187 assert.NoError(t, err, "Unexpected error getting server failure counter") 188 sf.Inc() 189 assert.Equal(t, int64(0), sf.Load(), "Expected to fall back to no-op metrics.") 190 191 e.latencies.Observe(0) 192 e.callerErrLatencies.Observe(0) 193 e.serverErrLatencies.Observe(0) 194 } 195 196 func TestUnknownIfEmpty(t *testing.T) { 197 tests := []struct { 198 transport string 199 expected string 200 }{ 201 { 202 expected: "unknown", 203 }, 204 { 205 transport: "tchannel", 206 expected: "tchannel", 207 }, 208 { 209 transport: "http", 210 expected: "http", 211 }, 212 { 213 transport: "grpc", 214 expected: "grpc", 215 }, 216 } 217 for _, tt := range tests { 218 t.Run(tt.expected, func(t *testing.T) { 219 actual := unknownIfEmpty(tt.transport) 220 assert.Equal(t, tt.expected, actual, "expected: %s, got: %s", tt.transport, actual) 221 222 }) 223 } 224 }