加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 在Ruby on Rails中执行每个控制器后获取响应状

发布时间:2020-12-17 02:57:14 所属栏目:百科 来源:网络整理
导读:我有一个RoR Rest API,我想发出一个指标,其中包含我的API的每个响应的状态.我设法为所有情况做到这一点,除了那些控制器崩溃的情况. 例如,ApplicationController中包含以下代码: require 'statsd-ruby'class ApplicationController ActionController::API af
我有一个RoR Rest API,我想发出一个指标,其中包含我的API的每个响应的状态.我设法为所有情况做到这一点,除了那些控制器崩溃的情况.

例如,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

但是这个解决方案不会捕获诸如ActionController :: RoutingError和ActiveRecord :: RecordNotFound之类的错误.

我尝试了以下代码:

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

但是当执行这个回调时,response.status值总是200(到目前为止,框架还没有设置它).

解决方法

知道rails logger设法做到这一点,我们可以看一下它的类 ActionController::LogSubscriber和process_action方法.因此,该事件中的状态可能为零,如果存在异常,我们可以看到它们如何将异常转换为状态:

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

所以现在,我们可以通过创建初始化程序,通过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

并用一个简单的控制器击中它几次:

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

产生一个文件:

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读