我有一个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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。