Kevin Trowbridge

Software Developer. Husband. I ❤ Webpages.

Monkeypatch NewRelic So That It Doesn’t Completely Hijack the ‘Process_action’ Method

| Comments

I recently upgraded a site from Rails 2 to Rails 3 and moved it to Heroku. Once the upgrade was complete, I eagerly opened up the NewRelic performance monitoring tools to see how much speedier the application had become.

To my dismay, the charts were all completely blank. I carried out the usual debugging steps … turned up the NewRelic agent logging verbosity … but I couldn’t find anything wrong. Then I discovered that NewRelic has a development mode —which logs requests made locally. I turned this on and took a look at these charts. They were also completely blank! So I realized that the problem was not that my application wasn’t properly reporting information to the NewRelic servers, the problem was that the application wasn’t even logging it correctly.

webpage with totally blank charts

I opened up RubyMine’s trusty ‘External Libraries’ menu and started placing breakpoints in the newrelic_rpm gem, hoping to find the source of the problem. Hours later I eventually discovered the problem:

NewRelic overrides the ActionController#process_action method to initialize its performance monitoring code. This trick is somewhat well known and other codebases may well fiddle with this method as well. In my case, the (Rails 2 era) user session / state (pre-Devise) code uses alias_method_chain on this method, renaming it to process_action_with_current_user_assignment and process_action_without_current_user_assignment. Renaming the method in this way was blocking NewRelic from working correctly.

Once I knew that this was the problem, I was able to come up with a MonkeyPatch to get NewRelic to allow other pieces of code to use alias_method_chain on the process_action method as well.

Place the following code into your /config/initializers directory:

config/initializers/newrelic_monkeypatch.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
module NewRelic
  module Agent
    module Instrumentation
      module Rails3
        module ActionController

          def self.newrelic_write_attr(attr_name, value) # :nodoc:
            write_inheritable_attribute(attr_name, value)
          end

          def self.newrelic_read_attr(attr_name) # :nodoc:
            read_inheritable_attribute(attr_name)
          end

          # determine the path that is used in the metric name for
          # the called controller action
          def newrelic_metric_path(action_name_override = nil)
            action_part = action_name_override || action_name
            if action_name_override || self.class.action_methods.include?(action_part)
              "#{self.class.controller_path}/#{action_part}"
            else
              "#{self.class.controller_path}/(other)"
            end
          end

          def process_action_with_newrelic_trace(*args)
            # skip instrumentation if we are in an ignored action
            if _is_filtered?('do_not_trace')
              NewRelic::Agent.disable_all_tracing do
                return process_action_without_newrelic_trace(*args)
              end
            end
            perform_action_with_newrelic_trace(:category => :controller, :name => self.action_name, :path => newrelic_metric_path, :params => request.filtered_parameters, :class_name => self.class.name) do
              process_action_without_newrelic_trace(*args)
            end
          end

        end
      end
    end
  end
end

DependencyDetection.defer do
  @name = :rails3_controller

  depends_on do
    defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
  end

  depends_on do
    defined?(ActionController) && defined?(ActionController::Base)
  end

  executes do
    NewRelic::Agent.logger.debug 'Installing Rails 3 Controller instrumentation'
  end

  executes do
    class ActionController::Base
      include NewRelic::Agent::Instrumentation::ControllerInstrumentation
      include NewRelic::Agent::Instrumentation::Rails3::ActionController
      alias_method_chain :process_action, :newrelic_trace
    end
  end
end

Credit to: https://gist.github.com/959784

software

Related

Comments