- コントローラの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