0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

特定のコントローラー#アクションのフィルター(コールバック関数)の実行時間をNew Relicにレポートするためのinitializerファイルを書いた

  • コントローラのbefore_actionとかで指定されているfilter(callback関数)の実行時間をNew Relicに送信するためのinitializerファイル
  • callback関数がProcで指定されている場合の処理がキツかった。(結局ProcをProcで挟み込むことで対応)
  • 黒魔術に黒魔術を重ねてます。動作確認はしていますが、正確な保証はないです。
config/initializers/trace_controller_filters.rb
# This initializer is to make a configuration to report the execution time of filters(callbacks)
# for all actions of specified Controller class.
#
# Controller(s) to be traced can be configurable by environment variable 'FILTER_TRACED_CONTROLLERS'
# Set it this way, say in .env file:
# (Ex) FILTER_TRACED_CONTROLLERS="ProjectsController Api::V2::ProjectsController UsersController"

Rails.application.config.after_initialize do
  if ENV['NEW_RELIC_ENABLED'] && ENV['FILTER_TRACED_CONTROLLERS'].present?
    traced_controllers = []
    ENV['FILTER_TRACED_CONTROLLERS'].split.each do |controller_str|
      begin
        traced_controllers << controller_str.constantize
      rescue
        puts "===== #{controller_str} in FILTER_TRACED_CONTROLLERS has not been found ====="
      end
    end

    traced_controllers.each do |controller|
      begin
        controller.class_eval do
          self.include ::NewRelic::Agent::MethodTracer
          self._process_action_callbacks().send(:chain).each do |callback|
            case callback.raw_filter
            when Symbol
              self.add_method_tracer callback.raw_filter
            when Proc
              unless callback.instance_variable_get(:@_already_wrapped)
                callback.instance_variable_set(:@_already_wrapped, true)
                original_filter = callback.raw_filter
                wrapped_filter = Proc.new{
                  # execution time of 'original_filter' should be traced only if 'self' (= a Controller instance
                  # which is executing current Proc) is an instance of one of FILTER_TRACED_CONTROLLERS.
                  if traced_controllers.include? self.class
                    self.class.trace_execution_scoped("Custom/ProcFilter/Proc_#{original_filter.source_location[0].gsub('/', ':')}}") do
                      # execute 'original_filter' in the context of 'self' (= a Controller instance which is executing current proc),
                      # which is not in the context of current 'wrapped_filter' Proc.
                      # just as is done originally in ActiveSupport::Callbacks#run_callbacks.
                      # See: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L645-L647
                      self.instance_exec(&original_filter)
                    end
                  else
                    self.instance_exec(&original_filter)
                  end
                }
                callback.instance_variable_set(:@filter, wrapped_filter)
                callback.instance_variable_set(:@key, callback.send(:compute_identifier, callback.raw_filter))
              end
            end
          end
        end
        puts "===== Filters of actions for #{controller} will be reported to New Relic ====="
      rescue
        puts "===== #{controller} in FILTER_TRACED_CONTROLLERS has not been found ====="
      end
    end
  end
end

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?