go.uber.org/yarpc@v1.72.1/internal/observability/stream.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 "io" 26 27 "go.uber.org/yarpc/api/transport" 28 "go.uber.org/zap" 29 ) 30 31 const ( 32 _successfulStreamReceive = "Successfully received stream message" 33 _successfulStreamSend = "Successfully sent stream message" 34 _errorStreamReceive = "Error receiving stream message" 35 _errorStreamSend = "Error sending stream message" 36 ) 37 38 var ( 39 _ transport.StreamCloser = (*streamWrapper)(nil) 40 _ transport.StreamHeadersSender = (*streamWrapper)(nil) 41 _ transport.StreamHeadersReader = (*streamWrapper)(nil) 42 ) 43 44 type streamWrapper struct { 45 transport.StreamCloser 46 47 call call 48 edge *streamEdge 49 logger *zap.Logger 50 } 51 52 func (c call) WrapClientStream(stream *transport.ClientStream) *transport.ClientStream { 53 wrapped, err := transport.NewClientStream(&streamWrapper{ 54 StreamCloser: stream, 55 call: c, 56 edge: c.edge.streaming, 57 logger: c.edge.logger, 58 }) 59 if err != nil { 60 // This will never happen since transport.NewClientStream only returns an 61 // error for nil streams. In the nearly impossible situation where it does, 62 // we fall back to using the original, unwrapped stream. 63 c.edge.logger.DPanic("transport.ClientStream wrapping should never fail, streaming metrics are disabled") 64 wrapped = stream 65 } 66 return wrapped 67 } 68 69 func (c call) WrapServerStream(stream *transport.ServerStream) *transport.ServerStream { 70 wrapped, err := transport.NewServerStream(&streamWrapper{ 71 StreamCloser: nopCloser{stream}, 72 call: c, 73 edge: c.edge.streaming, 74 logger: c.edge.logger, 75 }) 76 if err != nil { 77 // This will never happen since transport.NewServerStream only returns an 78 // error for nil streams. In the nearly impossible situation where it does, 79 // we fall back to using the original, unwrapped stream. 80 c.edge.logger.DPanic("transport.ServerStream wrapping should never fail, streaming metrics are disabled") 81 wrapped = stream 82 } 83 return wrapped 84 } 85 86 func (s *streamWrapper) SendMessage(ctx context.Context, msg *transport.StreamMessage) error { 87 // TODO: handle panic for metrics 88 if s.call.direction == _directionInbound && msg != nil { 89 s.edge.streamResponsePayloadSizes.IncBucket(int64(msg.BodySize)) 90 } 91 92 err := s.StreamCloser.SendMessage(ctx, msg) 93 s.call.logStreamEvent(err, err == nil, _successfulStreamSend, _errorStreamSend) 94 95 s.edge.sends.Inc() 96 if err == nil { 97 s.edge.sendSuccesses.Inc() 98 return nil 99 } 100 101 if sendFailuresCounter, err2 := s.edge.sendFailures.Get(_error, errToMetricString(err)); err2 != nil { 102 s.logger.DPanic("could not retrieve send failure counter", zap.Error(err2)) 103 } else { 104 sendFailuresCounter.Inc() 105 } 106 return err 107 } 108 109 func (s *streamWrapper) ReceiveMessage(ctx context.Context) (*transport.StreamMessage, error) { 110 // TODO: handle panic for metrics 111 msg, err := s.StreamCloser.ReceiveMessage(ctx) 112 if err == nil && msg != nil && s.call.direction == _directionInbound { 113 s.edge.streamRequestPayloadSizes.IncBucket(int64(msg.BodySize)) 114 } 115 // Receiving EOF does not constitute an error for the purposes of metrics and alerts. 116 // This is the only special case. 117 // All other log events treat EOF as an error, including when sending a 118 // message or concluding a handshake. 119 success := err == nil || err == io.EOF 120 s.call.logStreamEvent(err, success, _successfulStreamReceive, _errorStreamReceive) 121 122 s.edge.receives.Inc() 123 if success { 124 s.edge.receiveSuccesses.Inc() 125 return msg, err 126 } 127 128 if recvFailureCounter, err2 := s.edge.receiveFailures.Get(_error, errToMetricString(err)); err2 != nil { 129 s.logger.DPanic("could not retrieve receive failure counter", zap.Error(err2)) 130 } else { 131 recvFailureCounter.Inc() 132 } 133 134 return msg, err 135 } 136 137 func (s *streamWrapper) Close(ctx context.Context) error { 138 err := s.StreamCloser.Close(ctx) 139 s.call.EndStream(err) 140 return err 141 } 142 143 func (s *streamWrapper) SendHeaders(headers transport.Headers) error { 144 return transport.SendStreamHeaders(s.StreamCloser, headers) 145 } 146 147 func (s *streamWrapper) Headers() (transport.Headers, error) { 148 return transport.ReadStreamHeaders(s.StreamCloser) 149 } 150 151 // This is a light wrapper so that we can re-use the same methods for 152 // instrumenting observability. The transport.ClientStream has an additional 153 // Close(ctx) method, unlike the transport.ServerStream. 154 type nopCloser struct { 155 transport.Stream 156 } 157 158 func (c nopCloser) Close(ctx context.Context) error { 159 return nil 160 } 161 162 func (c nopCloser) SendHeaders(headers transport.Headers) error { 163 return transport.SendStreamHeaders(c.Stream, headers) 164 }