在Rails应用程序的前端获取实时日志 [英] Get Live logs in front end on rails application

查看:148
本文介绍了在Rails应用程序的前端获取实时日志的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在rails 3.x应用程序中,我正在使用net :: ssh并向远程pc运行一些命令.我想向用户的浏览器显示实时日志.例如,如果两个命令正在net中运行: :ssh执行,即通过echo "Hello"echo "Bye",然后在执行完后立即在浏览器中显示"Hello".这是我用于ssh连接和在ruby on rails应用程序中运行命令的代码

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  

这里on_data在每次执行命令时都会发送数据,我需要将此数据立即发送到浏览器.有什么方法可以做到这一点,以便可以在前端浏览器中实现实时日志.谢谢!

解决方案

要实现立即将数据发送到浏览器",您需要使用以下三种策略之一:

  • 短轮询
  • 流式传输
  • 长时间轮询

(在以下示例中,我假设$logs的类型为 Queue

短轮询

短轮询是从服务器获取未决信息的经典方法.这意味着,每隔X秒客户端就会询问服务器您是否有新信息?",服务器会回答否",在这种情况下,客户端会再次进入睡眠状态X秒钟,或者服务器会回答是",这是一些新信息...",客户端可以将其呈现出来,然后再休眠X秒钟.

此策略的主要优点是所有浏览器和所有ruby版本(简单的AJAX调用)都支持该策略.

此策略的缺点是,您在看到新数据之前将有X秒钟的延迟.如果X太短-您将遭受很多网络开销(所有请求都带有空响应).

示例实现:

def get_logs
  available_logs = []
  while(line = $logs.pop(true) rescue nil) available_logs << line

  body available_logs.join($/)
end

流式传输

流策略是当客户端向服务器打开请求时,服务器开始向客户端发送响应,但是当可用信息结束时,它不会关闭连接,而是保持连接状态.然后,当信息到来时,它将继续在打开的套接字上信息.

在Rails 4.0中,有一个 ActionController :: Live 可以实现战略.对于Rails 3.2,您可以尝试查看以下答案: https://stackoverflow.com/a/4320399/1120015

示例实现:

def get_logs
  class LogConsumer
    def each
      while $channel.active?
        yield $logs.pop + $/
      end
    end
  end

  self.response_body = LogConsumer.new
end

此解决方案与其他两个解决方案之间的主要区别在于,客户端实现不是那么简单-客户端不能仅等待响应返回然后呈现它(如默认的jQuery用法).对于示例实现,很多地方都指向 Ajax模式,目前显然不可用:-(.

我看到的另一个选项是使用门户插件*:

 portal.open("/get_logs", {
  inbound: function(data) {
    render_log_lines(data);
  }
});
 

*我没有使用此插件的经验,因此应将其作为研究的方向,而不是作为可行的示例...

长轮询

长时间轮询意味着您的AJAX客户端向服务器请求下一个日志行.如果服务器有一个(或多个)服务器要提供,它将服务器返回给客户端,客户端将渲染该行,然后再次请求.如果服务器没有要提供的行,则它不会返回空响​​应,而是挂起请求,然后等待直到有一行可用.一旦有新行可用,就会将其返回给客户端.客户端渲染线,然后立即再次请求服务器.

选择此策略时,必须确保在Web服务器或客户端中,以及中间的任何其他点(负载均衡器等)中均未设置timeout.

示例实现:

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

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:

  • Short Polling
  • Streaming
  • Long polling

(in the following examples I assume that $logs is of type Queue

Short Polling

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

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

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

这篇关于在Rails应用程序的前端获取实时日志的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆