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  }