[英]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.