简体   繁体   English

在Ruby on Rails中执行每个控制器后获取响应状态

[英]Get response status after every controller is executed in Ruby on Rails

I have a RoR Rest API and I want to emit a metric with the status of each response of my API. 我有一个RoR Rest API,我想发出一个指标,其中包含我的API的每个响应的状态。 I managed to do that for all the cases except from those where the controller is crashing. 我设法为所有情况做到这一点,除了那些控制器崩溃的情况。

For example with the following code in the ApplicationController: 例如,ApplicationController中包含以下代码:

require 'statsd-ruby'

class ApplicationController < ActionController::API
  after_action :push_status_metric

  def push_status_metric
    statsd = Statsd.new ENV['STATSD_LOCATION'], ENV['STATSD_PORT']
    puts normalize_status_metric(response.status)

    statsd.increment('ds.status.' + normalize_status_metric(response.status).to_s + '.int') unless request.fullpath == '/health'
  end

  private

  def normalize_status_metric(status)
    return 100 if status >= 100 && status < 200
    return 200 if status >= 200 && status < 300
    return 300 if status >= 300 && status < 400
    return 400 if status >= 400 && status < 500
    return 500 if status >= 500 && status < 600
    0
  end
end

But this solution doesn't capture errors such as ActionController::RoutingError and ActiveRecord::RecordNotFound. 但是这个解决方案不会捕获诸如ActionController :: RoutingError和ActiveRecord :: RecordNotFound之类的错误。

I tried the following code: 我尝试了以下代码:

  rescue_from StandardError do |exception|
    statsd = Statsd.new ENV['STATSD_LOCATION'], ENV['STATSD_PORT']
    statsd.increment('ds.status.' + normalize_status_metric(response.status).to_s + '.int') unless request.fullpath == '/health'

    raise exception
  end

But when this callback is executed, the response.status value is always 200 (seems like it's not set yet by the framework up to this point). 但是当执行这个回调时,response.status值总是200(到目前为止,框架还没有设置它)。

Knowing the the rails logger manages to do this, we can take a look at its class, ActionController::LogSubscriber and that process_action method. 知道rails logger设法做到这一点,我们可以看一下它的类, ActionController::LogSubscriber和那个process_action方法。 So, the status in that event could be nil , and we can see how they then convert an exception to a status, if an exception exists: 因此,该事件中的状态可能nil ,如果存在异常,我们可以看到它们如何将异常转换为状态:

status = payload[:status]
if status.nil? && payload[:exception].present?
  exception_class_name = payload[:exception].first
  status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end

So now, we can do something similar by subscribing to this event on our own, with Active Support Instrumentation , by creating an initializer: 现在,我们可以通过创建初始化程序,通过Active Support Instrumentation自行订阅此事件来做类似的事情:

ActiveSupport::Notifications.subscribe 'process_action.action_controller' do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)

  # opening a file here is probably a performance nightmare, but you'd be doing something with statsd, not a file, anyway
  open('metrics.txt', 'a') do |f|
    # get the action status, this code is from the ActionController::LogSubscriber
    status = event.payload[:status]

    if status.nil? && event.payload[:exception].present?
      exception_class_name = event.payload[:exception].first

      status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
    end

    f.puts "process_action.action_controller controller: #{event.payload[:controller]} - action: #{event.payload[:action]} - path: #{event.payload[:path]} - status: #{status}"
  end
end

and hitting it a few times with a simple controller: 并用一个简单的控制器击中它几次:

class HomeController < ApplicationController
  def non_standard_status
    render html: 'This is not fine', status: :forbidden
  end

  def error
    raise ActiveRecord::RecordNotFound, 'No Records Found'
  end

  def another_error
    raise ArgumentError, 'Some Argument is wrong'
  end

  def this_is_fine
    render html: 'This is fine'
  end
end

yields a file: 产生一个文件:

process_action.action_controller controller: HomeController - action: non_standard_status - path: /forbidden - status: 403
process_action.action_controller controller: HomeController - action: error - path: /error - status: 404
process_action.action_controller controller: HomeController - action: another_error - path: /error2 - status: 500
process_action.action_controller controller: HomeController - action: this_is_fine - path: /fine - status: 200

But when this callback is executed, the response.status value is always 200 (seems like it's not set yet by the framework up to this point). 但是当执行这个回调时,response.status值总是200(到目前为止,框架还没有设置它)。

Correct, it's not yet set (application didn't attempt to render). 正确,它尚未设置(应用程序没有尝试渲染)。 Normally, it's the rescue_from handler that will render the "error" response, with code and everything. 通常,它是rescue_from处理程序,它将使用代码和所有内容呈现“错误”响应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM