Here in the rails 3.x app,I am using net::ssh and running some commands to remote pc.I would like to display the live logs to the user's browser.like,If two commands are running in net::ssh to execute i.e echo "Hello"
,echo "Bye"
is passed then "Hello" should be displayed in the browser immediately finishing after its execution.Here is the code I am using for ssh connection and running commands in ruby on rails application
Net::SSH.start( @servers['local'] , @machine_name, :password => @machine_pwd, :timeout => 30) do |ssh|
ssh.open_channel do |channel|
channel.request_pty
channel.exec("echo 'ssh started'")
channel.exec("ruby -v")
channel.exec("rails -v")
channel.exec("echo 'ssh Finished'")
channel.on_close do |ch|
puts "****shell terminated****"
end
channel.on_eof do |ch|
puts "****remote end is done sending data****"
end
channel.on_extended_data do |ch, type, data|
puts "****got stderr****: #{data.inspect}"
end
channel.on_data do |channel, data|
puts "****ondata****: #{data} \n"
puts "****channel****: #{channel} \n"
$logs << data # this data to be sent immediately to browser in each go
end
ssh.loop
end
end
Here on_data is sending data in every command execution,I need this data to be sent to the browser immediately.Is there any way to do this.So that I can achieve live logs in front end browser.Thanks!
To achieve 'sending data to the browser immediately' you need to use one of three strategies:
(in the following examples I assume that $logs
is of type Queue
Short polling is the classic way to get pending information from the server. It means that once every X seconds the client asks the server 'do you have new information?', the server answers either 'no', in which case the client goes back to sleep for another X seconds, or the server answers 'yes, here is some new information...', which the client can then render, before sleeping for another X seconds.
The major upside for this strategy is that it is supported by all browsers and all ruby versions (simple AJAX calls).
The downside of this strategy is that you are going to have a delay of up to X seconds before seeing new data. If X is too short - you are going to suffer from a lot of network overhead (all the requests with empty responses).
Example implementation:
def get_logs
available_logs = []
while(line = $logs.pop(true) rescue nil) available_logs << line
body available_logs.join($/)
end
Streaming strategy is when your client opens a request to the server, the server starts sending a response to the client, but when the available information ends, it doesn't close the connection, but leaves it open. Then it continues to stream information on the open socket as the information comes.
In Rails 4.0 there is an ActionController::Live which implements this strategy. For Rails 3.2 you can try looking at this answer: https://stackoverflow.com/a/4320399/1120015
Example implementation:
def get_logs
class LogConsumer
def each
while $channel.active?
yield $logs.pop + $/
end
end
end
self.response_body = LogConsumer.new
end
The main difference between this solution and the other two is that the client implementation is not as straight forward - the client can't just wait for the response to return and then render it (like the default jQuery
usage). For sample implementation, a lot of places point to Ajax Patterns, which apparently is unavailable at the moment :-(.
Another option I've seen is using the portal plugin*:
portal.open("/get_logs", {
inbound: function(data) {
render_log_lines(data);
}
});
* I have no experience using this plugin, so this should be taken as a direction to research, rather than as a working example...
Long polling means that your AJAX client request the server for the next log line. If the server has one (or more) to provide, it returns it to the client, the client renders the line(s), and requests again. If the server does not have a line to provide, it does not return an empty response, but rather hangs to the request, and waits until a line is available. As soon as a new line is available, it is returned to the client. The client renders the line, then immediately requests the server again.
When you choose this strategy, you must make sure that there is no timeout
set in either the web-server or the client, nor in any other points in the middle (load balancers, etc.)
Example implementation:
def get_logs
available_logs = []
while(line = $logs.pop(true) rescue nil) available_logs << line
if available_logs.empty?
available_logs << $logs.pop
end
body available_logs.join($/)
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With