grpc/rpclog/grpc.go
func StreamClientInterceptor(log ttnlog.Interface) grpc.StreamClientInterceptor {
	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (stream grpc.ClientStream, err error) {
		log := getLog(log).WithField("method", method)
		log = log.WithFields(FieldsFromOutgoingContext(ctx))
		log.Debug("rpc-client: stream starting")
		stream, err = streamer(ctx, desc, cc, method, opts...)
		if err != nil {
			if err == context.Canceled || grpc.Code(err) == codes.Canceled {
				log.Debug("rpc-client: stream canceled")
				return
			}
			log.WithError(err).Debug("rpc-client: stream failed")
			return
		}
		go func() {
			<-stream.Context().Done()
			if err := stream.Context().Err(); err != nil {
				log = log.WithError(err)
			}
			log.Debug("rpc-client: stream done")
		}()
		return
	}
}
func StreamClientInterceptor(log ttnlog.Interface) grpc.StreamClientInterceptor {
	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (stream grpc.ClientStream, err error) {
		log := getLog(log).WithField("method", method)
		log = log.WithFields(FieldsFromOutgoingContext(ctx))
		log.Debug("rpc-client: stream starting")
		stream, err = streamer(ctx, desc, cc, method, opts...)
		if err != nil {
			if err == context.Canceled || grpc.Code(err) == codes.Canceled {
				log.Debug("rpc-client: stream canceled")
				return
			}
			log.WithError(err).Debug("rpc-client: stream failed")
			return
		}
		go func() {
			<-stream.Context().Done()
			if err := stream.Context().Err(); err != nil {
				log = log.WithError(err)
			}
			log.Debug("rpc-client: stream done")
		}()
		return
	}
}
// StreamClientInterceptor logs streaming RPCs on the client side