honkster-newrelic_rpm 2.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/CHANGELOG +462 -0
  2. data/LICENSE +37 -0
  3. data/README.rdoc +172 -0
  4. data/bin/mongrel_rpm +33 -0
  5. data/bin/newrelic +13 -0
  6. data/bin/newrelic_cmd +5 -0
  7. data/cert/cacert.pem +34 -0
  8. data/install.rb +9 -0
  9. data/lib/new_relic/agent.rb +382 -0
  10. data/lib/new_relic/agent/agent.rb +741 -0
  11. data/lib/new_relic/agent/busy_calculator.rb +91 -0
  12. data/lib/new_relic/agent/chained_call.rb +13 -0
  13. data/lib/new_relic/agent/error_collector.rb +131 -0
  14. data/lib/new_relic/agent/instrumentation/active_merchant.rb +18 -0
  15. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +92 -0
  16. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +45 -0
  17. data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
  18. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +409 -0
  19. data/lib/new_relic/agent/instrumentation/data_mapper.rb +58 -0
  20. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +22 -0
  21. data/lib/new_relic/agent/instrumentation/memcache.rb +40 -0
  22. data/lib/new_relic/agent/instrumentation/merb/controller.rb +26 -0
  23. data/lib/new_relic/agent/instrumentation/merb/errors.rb +9 -0
  24. data/lib/new_relic/agent/instrumentation/metric_frame.rb +319 -0
  25. data/lib/new_relic/agent/instrumentation/net.rb +17 -0
  26. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +22 -0
  27. data/lib/new_relic/agent/instrumentation/rack.rb +98 -0
  28. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +59 -0
  29. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
  30. data/lib/new_relic/agent/instrumentation/rails/errors.rb +24 -0
  31. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +45 -0
  32. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +21 -0
  33. data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -0
  34. data/lib/new_relic/agent/instrumentation/sunspot.rb +17 -0
  35. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +10 -0
  36. data/lib/new_relic/agent/method_tracer.rb +350 -0
  37. data/lib/new_relic/agent/sampler.rb +50 -0
  38. data/lib/new_relic/agent/samplers/cpu_sampler.rb +54 -0
  39. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +37 -0
  40. data/lib/new_relic/agent/samplers/memory_sampler.rb +142 -0
  41. data/lib/new_relic/agent/samplers/object_sampler.rb +24 -0
  42. data/lib/new_relic/agent/shim_agent.rb +25 -0
  43. data/lib/new_relic/agent/stats_engine.rb +24 -0
  44. data/lib/new_relic/agent/stats_engine/metric_stats.rb +118 -0
  45. data/lib/new_relic/agent/stats_engine/samplers.rb +83 -0
  46. data/lib/new_relic/agent/stats_engine/transactions.rb +149 -0
  47. data/lib/new_relic/agent/transaction_sampler.rb +330 -0
  48. data/lib/new_relic/agent/worker_loop.rb +81 -0
  49. data/lib/new_relic/collection_helper.rb +71 -0
  50. data/lib/new_relic/command.rb +85 -0
  51. data/lib/new_relic/commands/deployments.rb +105 -0
  52. data/lib/new_relic/commands/install.rb +81 -0
  53. data/lib/new_relic/control.rb +203 -0
  54. data/lib/new_relic/control/configuration.rb +149 -0
  55. data/lib/new_relic/control/frameworks/external.rb +13 -0
  56. data/lib/new_relic/control/frameworks/merb.rb +24 -0
  57. data/lib/new_relic/control/frameworks/rails.rb +126 -0
  58. data/lib/new_relic/control/frameworks/rails3.rb +60 -0
  59. data/lib/new_relic/control/frameworks/ruby.rb +36 -0
  60. data/lib/new_relic/control/frameworks/sinatra.rb +18 -0
  61. data/lib/new_relic/control/instrumentation.rb +95 -0
  62. data/lib/new_relic/control/logging_methods.rb +74 -0
  63. data/lib/new_relic/control/profiling.rb +24 -0
  64. data/lib/new_relic/control/server_methods.rb +88 -0
  65. data/lib/new_relic/delayed_job_injection.rb +27 -0
  66. data/lib/new_relic/histogram.rb +91 -0
  67. data/lib/new_relic/local_environment.rb +333 -0
  68. data/lib/new_relic/merbtasks.rb +6 -0
  69. data/lib/new_relic/metric_data.rb +42 -0
  70. data/lib/new_relic/metric_parser.rb +136 -0
  71. data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
  72. data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
  73. data/lib/new_relic/metric_parser/active_record.rb +28 -0
  74. data/lib/new_relic/metric_parser/apdex.rb +88 -0
  75. data/lib/new_relic/metric_parser/controller.rb +62 -0
  76. data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
  77. data/lib/new_relic/metric_parser/errors.rb +6 -0
  78. data/lib/new_relic/metric_parser/external.rb +50 -0
  79. data/lib/new_relic/metric_parser/mem_cache.rb +50 -0
  80. data/lib/new_relic/metric_parser/other_transaction.rb +36 -0
  81. data/lib/new_relic/metric_parser/view.rb +61 -0
  82. data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
  83. data/lib/new_relic/metric_parser/web_service.rb +9 -0
  84. data/lib/new_relic/metric_spec.rb +67 -0
  85. data/lib/new_relic/metrics.rb +9 -0
  86. data/lib/new_relic/noticed_error.rb +24 -0
  87. data/lib/new_relic/rack/developer_mode.rb +257 -0
  88. data/lib/new_relic/rack/metric_app.rb +64 -0
  89. data/lib/new_relic/rack/mongrel_rpm.ru +26 -0
  90. data/lib/new_relic/rack/newrelic.yml +27 -0
  91. data/lib/new_relic/rack_app.rb +6 -0
  92. data/lib/new_relic/recipes.rb +82 -0
  93. data/lib/new_relic/stats.rb +368 -0
  94. data/lib/new_relic/timer_lib.rb +27 -0
  95. data/lib/new_relic/transaction_analysis.rb +119 -0
  96. data/lib/new_relic/transaction_sample.rb +586 -0
  97. data/lib/new_relic/url_rule.rb +14 -0
  98. data/lib/new_relic/version.rb +55 -0
  99. data/lib/new_relic_api.rb +276 -0
  100. data/lib/newrelic_rpm.rb +49 -0
  101. data/lib/tasks/all.rb +4 -0
  102. data/lib/tasks/install.rake +7 -0
  103. data/lib/tasks/tests.rake +15 -0
  104. data/newrelic.yml +246 -0
  105. data/newrelic_rpm.gemspec +254 -0
  106. data/recipes/newrelic.rb +6 -0
  107. data/test/active_record_fixtures.rb +55 -0
  108. data/test/config/newrelic.yml +48 -0
  109. data/test/config/test_control.rb +36 -0
  110. data/test/new_relic/agent/active_record_instrumentation_test.rb +286 -0
  111. data/test/new_relic/agent/agent_controller_test.rb +294 -0
  112. data/test/new_relic/agent/agent_test_controller.rb +77 -0
  113. data/test/new_relic/agent/busy_calculator_test.rb +81 -0
  114. data/test/new_relic/agent/collection_helper_test.rb +125 -0
  115. data/test/new_relic/agent/error_collector_test.rb +163 -0
  116. data/test/new_relic/agent/memcache_instrumentation_test.rb +103 -0
  117. data/test/new_relic/agent/method_tracer_test.rb +340 -0
  118. data/test/new_relic/agent/metric_data_test.rb +53 -0
  119. data/test/new_relic/agent/metric_frame_test.rb +51 -0
  120. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  121. data/test/new_relic/agent/net_instrumentation_test.rb +77 -0
  122. data/test/new_relic/agent/rpm_agent_test.rb +142 -0
  123. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
  124. data/test/new_relic/agent/stats_engine/samplers_test.rb +72 -0
  125. data/test/new_relic/agent/stats_engine/stats_engine_test.rb +184 -0
  126. data/test/new_relic/agent/task_instrumentation_test.rb +188 -0
  127. data/test/new_relic/agent/testable_agent.rb +13 -0
  128. data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
  129. data/test/new_relic/agent/transaction_sample_test.rb +192 -0
  130. data/test/new_relic/agent/transaction_sampler_test.rb +385 -0
  131. data/test/new_relic/agent/worker_loop_test.rb +66 -0
  132. data/test/new_relic/control_test.rb +127 -0
  133. data/test/new_relic/deployments_api_test.rb +69 -0
  134. data/test/new_relic/environment_test.rb +75 -0
  135. data/test/new_relic/metric_parser_test.rb +226 -0
  136. data/test/new_relic/metric_spec_test.rb +177 -0
  137. data/test/new_relic/rack/episodes_test.rb +318 -0
  138. data/test/new_relic/shim_agent_test.rb +9 -0
  139. data/test/new_relic/stats_test.rb +312 -0
  140. data/test/new_relic/version_number_test.rb +89 -0
  141. data/test/test_contexts.rb +28 -0
  142. data/test/test_helper.rb +72 -0
  143. data/ui/helpers/developer_mode_helper.rb +359 -0
  144. data/ui/helpers/google_pie_chart.rb +49 -0
  145. data/ui/views/layouts/newrelic_default.rhtml +47 -0
  146. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  147. data/ui/views/newrelic/_sample.rhtml +20 -0
  148. data/ui/views/newrelic/_segment.rhtml +29 -0
  149. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  150. data/ui/views/newrelic/_segment_row.rhtml +14 -0
  151. data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
  152. data/ui/views/newrelic/_show_sample_sql.rhtml +20 -0
  153. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  154. data/ui/views/newrelic/_sql_row.rhtml +16 -0
  155. data/ui/views/newrelic/_stack_trace.rhtml +15 -0
  156. data/ui/views/newrelic/_table.rhtml +12 -0
  157. data/ui/views/newrelic/explain_sql.rhtml +43 -0
  158. data/ui/views/newrelic/file/images/arrow-close.png +0 -0
  159. data/ui/views/newrelic/file/images/arrow-open.png +0 -0
  160. data/ui/views/newrelic/file/images/blue_bar.gif +0 -0
  161. data/ui/views/newrelic/file/images/file_icon.png +0 -0
  162. data/ui/views/newrelic/file/images/gray_bar.gif +0 -0
  163. data/ui/views/newrelic/file/images/new-relic-rpm-desktop.gif +0 -0
  164. data/ui/views/newrelic/file/images/new_relic_rpm_desktop.gif +0 -0
  165. data/ui/views/newrelic/file/images/textmate.png +0 -0
  166. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  167. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  168. data/ui/views/newrelic/file/stylesheets/style.css +484 -0
  169. data/ui/views/newrelic/index.rhtml +59 -0
  170. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  171. data/ui/views/newrelic/show_sample.rhtml +79 -0
  172. data/ui/views/newrelic/show_source.rhtml +3 -0
  173. data/ui/views/newrelic/threads.rhtml +52 -0
  174. metadata +307 -0
@@ -0,0 +1,17 @@
1
+ if defined? Net::HTTP
2
+ Net::HTTP.class_eval do
3
+ def request_with_newrelic_trace(*args, &block)
4
+ metrics = ["External/#{@address}/Net::HTTP/#{args[0].method}","External/#{@address}/all"]
5
+ if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
6
+ metrics << "External/allWeb"
7
+ else
8
+ metrics << "External/allOther"
9
+ end
10
+ self.class.trace_execution_scoped metrics do
11
+ request_without_newrelic_trace(*args, &block)
12
+ end
13
+ end
14
+ alias request_without_newrelic_trace request
15
+ alias request request_with_newrelic_trace
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ if defined?(PhusionPassenger)
2
+ NewRelic::Agent.logger.debug "Installing Passenger event hooks."
3
+
4
+ PhusionPassenger.on_event(:stopping_worker_process) do
5
+ NewRelic::Agent.logger.debug "Passenger stopping this process, shutdown the agent."
6
+ NewRelic::Agent.instance.shutdown
7
+ end
8
+
9
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
10
+ # We want to reset the stats from the stats engine in case any carried
11
+ # over into the spawned process. Don't clear them in case any were
12
+ # cached. We do this even in conservative spawning.
13
+ NewRelic::Agent.after_fork(:force_reconnect => true)
14
+ end
15
+
16
+ elsif (defined?(::Passenger) && defined?(::Passenger::AbstractServer)) || defined?(::IN_PHUSION_PASSENGER)
17
+ # We're on an older version of passenger
18
+ NewRelic::Agent.logger.warn "An older version of Phusion Passenger has been detected. We recommend using at least release 2.1.1."
19
+
20
+ NewRelic::Agent::Instrumentation::MetricFrame.check_server_connection = true
21
+
22
+ end
@@ -0,0 +1,98 @@
1
+ require 'new_relic/agent/instrumentation/controller_instrumentation'
2
+
3
+ module NewRelic
4
+ module Agent
5
+ module Instrumentation
6
+ # == Instrumentation for Rack
7
+ #
8
+ # New Relic will instrument a #call method as if it were a controller
9
+ # action, collecting transaction traces and errors. The middleware will
10
+ # be identified only by it's class, so if you want to instrument multiple
11
+ # actions in a middleware, you need to use
12
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation::ClassMethods#add_transaction_tracer
13
+ #
14
+ # Example:
15
+ # require 'newrelic_rpm'
16
+ # require 'new_relic/agent/instrumentation/rack'
17
+ # class Middleware
18
+ # def call(env)
19
+ # ...
20
+ # end
21
+ # # Do the include after the call method is defined:
22
+ # include NewRelic::Agent::Instrumentation::Rack
23
+ # end
24
+ #
25
+ # == Instrumenting Metal and Cascading Middlewares
26
+ #
27
+ # Metal apps and apps belonging to Rack::Cascade middleware
28
+ # follow a convention of returning a 404 for all requests except
29
+ # the ones they are set up to handle. This means that New Relic
30
+ # needs to ignore these calls when they return a 404.
31
+ #
32
+ # In these cases, you should not include or extend the Rack
33
+ # module but instead include
34
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation.
35
+ # Here's how that might look for a Metal app:
36
+ #
37
+ # require 'new_relic/agent/instrumentation/controller_instrumentation'
38
+ # class MetalApp
39
+ # extend NewRelic::Agent::Instrumentation::ControllerInstrumentation
40
+ # def self.call(env)
41
+ # if should_do_my_thing?
42
+ # perform_action_with_newrelic_trace(:category => :rack) do
43
+ # return my_response(env)
44
+ # end
45
+ # else
46
+ # return [404, {"Content-Type" => "text/html"}, ["Not Found"]]
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # == Overriding the metric name
52
+ #
53
+ # By default the middleware is identified only by its class, but if you want to
54
+ # be more specific and pass in name, then omit including the Rack instrumentation
55
+ # and instead follow this example:
56
+ #
57
+ # require 'newrelic_rpm'
58
+ # require 'new_relic/agent/instrumentation/controller_instrumentation'
59
+ # class Middleware
60
+ # include NewRelic::Agent::Instrumentation::ControllerInstrumentation
61
+ # def call(env)
62
+ # ...
63
+ # end
64
+ # add_transaction_tracer :call, :category => :rack, :name => 'my app'
65
+ # end
66
+ #
67
+ module Rack
68
+ def newrelic_request_headers
69
+ @newrelic_request.env
70
+ end
71
+ def call_with_newrelic(*args)
72
+ @newrelic_request = ::Rack::Request.new(args.first)
73
+ perform_action_with_newrelic_trace(:category => :rack, :request => @newrelic_request) do
74
+ result = call_without_newrelic(*args)
75
+ # Ignore cascaded calls
76
+ MetricFrame.abort_transaction! if result.first == 404
77
+ result
78
+ end
79
+ end
80
+ def self.included middleware #:nodoc:
81
+ middleware.class_eval do
82
+ alias call_without_newrelic call
83
+ alias call call_with_newrelic
84
+ end
85
+ end
86
+ include ControllerInstrumentation
87
+ def self.extended middleware #:nodoc:
88
+ middleware.class_eval do
89
+ class << self
90
+ alias call_without_newrelic call
91
+ alias call call_with_newrelic
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,59 @@
1
+
2
+ if defined? ActionController
3
+
4
+ case Rails::VERSION::STRING
5
+
6
+ when /^(1\.|2\.0)/ # Rails 1.* - 2.0
7
+ ActionController::Base.class_eval do
8
+ add_method_tracer :render, 'View/#{newrelic_metric_path}/Rendering'
9
+ end
10
+
11
+ when /^2\.1\./ # Rails 2.1
12
+ ActionView::PartialTemplate.class_eval do
13
+ add_method_tracer :render, 'View/#{path_without_extension[%r{^(/.*/)?(.*)$},2]}.#{@view.template_format}.#{extension}/Partial'
14
+ end
15
+ # this is for template rendering, as opposed to partial rendering.
16
+ ActionView::Template.class_eval do
17
+ add_method_tracer :render, 'View/#{(path_without_extension || @view.controller.newrelic_metric_path)[%r{^(/.*/)?(.*)$},2]}.#{@view.template_format}.#{extension}/Rendering'
18
+ end
19
+
20
+ when /^2\./ # Rails 2.2-2.*
21
+ ActionView::RenderablePartial.module_eval do
22
+ add_method_tracer :render_partial, 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Partial'
23
+ end
24
+ ActionView::Template.class_eval do
25
+ add_method_tracer :render, 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering'
26
+ end
27
+ end unless NewRelic::Control.instance['disable_view_instrumentation']
28
+
29
+ ActionController::Base.class_eval do
30
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
31
+
32
+ # Compare with #alias_method_chain, which is not available in
33
+ # Rails 1.1:
34
+ alias_method :perform_action_without_newrelic_trace, :perform_action
35
+ alias_method :perform_action, :perform_action_with_newrelic_trace
36
+ private :perform_action
37
+
38
+ def self.newrelic_write_attr(attr_name, value) # :nodoc:
39
+ write_inheritable_attribute(attr_name, value)
40
+ end
41
+
42
+ def self.newrelic_read_attr(attr_name) # :nodoc:
43
+ read_inheritable_attribute(attr_name)
44
+ end
45
+
46
+ # determine the path that is used in the metric name for
47
+ # the called controller action
48
+ def newrelic_metric_path(action_name_override = nil)
49
+ action_part = action_name_override || action_name
50
+ if action_name_override || self.class.action_methods.include?(action_part)
51
+ "#{self.class.controller_path}/#{action_part}"
52
+ else
53
+ "#{self.class.controller_path}/(other)"
54
+ end
55
+ end
56
+ end
57
+ else
58
+ NewRelic::Agent.instance.log.debug "WARNING: ActionController instrumentation not added"
59
+ end
@@ -0,0 +1,27 @@
1
+ # NewRelic Agent instrumentation for WebServices
2
+
3
+ # Note Action Web Service is removed from default package in rails 2.0
4
+ if defined? ActionWebService
5
+
6
+ # instrumentation for Web Service martialing - XML RPC
7
+ ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.class_eval do
8
+ add_method_tracer :decode_request, "WebService/Xml Rpc/XML Decode"
9
+ add_method_tracer :encode_request, "WebService/Xml Rpc/XML Encode"
10
+ add_method_tracer :decode_response, "WebService/Xml Rpc/XML Decode"
11
+ add_method_tracer :encode_response, "WebService/Xml Rpc/XML Encode"
12
+ end
13
+
14
+ # instrumentation for Web Service martialing - Soap
15
+ ActionWebService::Protocol::Soap::SoapProtocol.class_eval do
16
+ add_method_tracer :decode_request, "WebService/Soap/XML Decode"
17
+ add_method_tracer :encode_request, "WebService/Soap/XML Encode"
18
+ add_method_tracer :decode_response, "WebService/Soap/XML Decode"
19
+ add_method_tracer :encode_response, "WebService/Soap/XML Encode"
20
+ end
21
+
22
+ ActionController::Base.class_eval do
23
+ if method_defined? :perform_invocation
24
+ add_method_tracer :perform_invocation, 'WebService/#{controller_name}/#{args.first}'
25
+ end
26
+ end if defined? ActionController::Base
27
+ end
@@ -0,0 +1,24 @@
1
+
2
+ ActionController::Base.class_eval do
3
+
4
+ # Make a note of an exception associated with the currently executing
5
+ # controller action. Note that this used to be available on Object
6
+ # but we replaced that global method with NewRelic::Agent#notice_error.
7
+ # Use that one outside of controller actions.
8
+ def newrelic_notice_error(exception, custom_params = {})
9
+ NewRelic::Agent::Instrumentation::MetricFrame.notice_error exception, :custom_params => custom_params, :request => request
10
+ end
11
+
12
+ def rescue_action_with_newrelic_trace(exception)
13
+ rescue_action_without_newrelic_trace exception
14
+ NewRelic::Agent::Instrumentation::MetricFrame.notice_error exception, :request => request
15
+ end
16
+
17
+ # Compare with #alias_method_chain, which is not available in
18
+ # Rails 1.1:
19
+ alias_method :rescue_action_without_newrelic_trace, :rescue_action
20
+ alias_method :rescue_action, :rescue_action_with_newrelic_trace
21
+ protected :rescue_action
22
+
23
+ end if defined? ActionController
24
+
@@ -0,0 +1,45 @@
1
+ module NewRelic
2
+ module Agent
3
+ module Instrumentation
4
+ module Rails3
5
+ module ActionController
6
+ def self.newrelic_write_attr(attr_name, value) # :nodoc:
7
+ write_inheritable_attribute(attr_name, value)
8
+ end
9
+
10
+ def self.newrelic_read_attr(attr_name) # :nodoc:
11
+ read_inheritable_attribute(attr_name)
12
+ end
13
+
14
+ # determine the path that is used in the metric name for
15
+ # the called controller action
16
+ def newrelic_metric_path(action_name_override = nil)
17
+ action_part = action_name_override || action_name
18
+ if action_name_override || self.class.action_methods.include?(action_part)
19
+ "#{self.class.controller_path}/#{action_part}"
20
+ else
21
+ "#{self.class.controller_path}/(other)"
22
+ end
23
+ end
24
+
25
+ def process_action(*args)
26
+
27
+ perform_action_with_newrelic_trace(:category => :controller, :name => self.action_name, :params => request.filtered_parameters, :class_name => self.class.name) do
28
+ super
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ if defined?(ActionController) && defined?(ActionController::Base)
39
+ puts "ApplicationController is defined"
40
+ class ActionController::Base
41
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
42
+ include NewRelic::Agent::Instrumentation::Rails3::ActionController
43
+ end
44
+ end
45
+
@@ -0,0 +1,21 @@
1
+ module NewRelic
2
+ module Agent
3
+ module Instrumentation
4
+ module Rails3
5
+ module Errors
6
+ def newrelic_notice_error(exception, custom_params = {})
7
+ filtered_params = (respond_to? :filter_parameters) ? filter_parameters(params) : params
8
+ filtered_params.merge!(custom_params)
9
+ NewRelic::Agent.agent.error_collector.notice_error(exception, request, newrelic_metric_path, filtered_params)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ if defined?(ActionController) && defined?(ActionController::Base)
18
+ class ActionController::Base
19
+ include NewRelic::Agent::Instrumentation::Rails3::Errors
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ if defined?(Sinatra::Base)
2
+ require 'new_relic/agent/instrumentation/controller_instrumentation'
3
+ module NewRelic
4
+ module Agent
5
+ module Instrumentation
6
+ # NewRelic instrumentation for Sinatra applications. Sinatra actions will
7
+ # appear in the UI similar to controller actions, and have breakdown charts
8
+ # and transaction traces.
9
+ #
10
+ # The actions in the UI will correspond to the pattern expression used
11
+ # to match them. HTTP operations are not distinguished. Multiple matches
12
+ # will all be tracked as separate actions.
13
+ module Sinatra
14
+
15
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
16
+
17
+ def route_eval_with_newrelic(&block_arg)
18
+ path = unescape(@request.path_info)
19
+ name = path
20
+ # Go through each route and look for a match
21
+ if routes = self.class.routes[@request.request_method]
22
+ routes.detect do |pattern, keys, conditions, block|
23
+ if block_arg.equal? block
24
+ name = pattern.source
25
+ end
26
+ end
27
+ end
28
+ # strip of leading ^ and / chars and trailing $ and /
29
+ name.gsub!(%r{^[/^]*(.*?)[/\$]*$}, '\1')
30
+ name = 'root' if name.empty?
31
+ perform_action_with_newrelic_trace(:category => :sinatra, :name => name) do
32
+ route_eval_without_newrelic(&block_arg)
33
+ end
34
+ end
35
+ end
36
+
37
+ ::Sinatra::Base.class_eval do
38
+ include NewRelic::Agent::Instrumentation::Sinatra
39
+ alias route_eval_without_newrelic route_eval
40
+ alias route_eval route_eval_with_newrelic
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ if defined?(::Sunspot)
2
+ ::Sunspot.module_eval do
3
+ class << self
4
+ %w(index index!).each do |method|
5
+ add_method_tracer method, 'SolrClient/Sunspot/index'
6
+ end
7
+ add_method_tracer :commit, 'SolrClient/Sunspot/commit'
8
+
9
+ %w[search more_like_this].each do |method|
10
+ add_method_tracer method, 'SolrClient/Sunspot/query'
11
+ end
12
+ %w[remove remove! remove_by_id remove_by_id! remove_all remove_all!].each do |method|
13
+ add_method_tracer method, 'SolrClient/Sunspot/delete'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ if defined?(Unicorn::HttpServer)
2
+ Unicorn::HttpServer.class_eval do
3
+ NewRelic::Agent.logger.debug "Installing Unicorn worker hook."
4
+ old_worker_loop = instance_method(:worker_loop)
5
+ define_method(:worker_loop) do | worker |
6
+ NewRelic::Agent.after_fork(:force_reconnect => true)
7
+ old_worker_loop.bind(self).call(worker)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,350 @@
1
+ require 'new_relic/control'
2
+ module NewRelic
3
+ module Agent
4
+ # This module contains class methods added to support installing custom
5
+ # metric tracers and executing for individual metrics.
6
+ #
7
+ # == Examples
8
+ #
9
+ # When the agent initializes, it extends Module with these methods.
10
+ # However if you want to use the API in code that might get loaded
11
+ # before the agent is initialized you will need to require
12
+ # this file:
13
+ #
14
+ # require 'new_relic/agent/method_tracer'
15
+ # class A
16
+ # include NewRelic::Agent::MethodTracer
17
+ # def process
18
+ # ...
19
+ # end
20
+ # add_method_tracer :process
21
+ # end
22
+ #
23
+ # To instrument a class method:
24
+ #
25
+ # require 'new_relic/agent/method_tracer'
26
+ # class An
27
+ # def self.process
28
+ # ...
29
+ # end
30
+ # class << self
31
+ # include NewRelic::Agent::MethodTracer
32
+ # add_method_tracer :process
33
+ # end
34
+ # end
35
+
36
+ module MethodTracer
37
+
38
+ def self.included clazz #:nodoc:
39
+ clazz.extend ClassMethods
40
+ clazz.send :include, InstanceMethods
41
+ end
42
+
43
+ def self.extended clazz #:nodoc:
44
+ clazz.extend ClassMethods
45
+ clazz.extend InstanceMethods
46
+ end
47
+
48
+ module InstanceMethods
49
+ # Deprecated: original method preserved for API backward compatibility.
50
+ # Use either #trace_execution_scoped or #trace_execution_unscoped
51
+ def trace_method_execution(metric_names, push_scope, produce_metric, deduct_call_time_from_parent, &block) #:nodoc:
52
+ if push_scope
53
+ trace_execution_scoped(metric_names, :metric => produce_metric,
54
+ :deduct_call_time_from_parent => deduct_call_time_from_parent, &block)
55
+ else
56
+ trace_execution_unscoped(metric_names, &block)
57
+ end
58
+ end
59
+
60
+ # Trace a given block with stats assigned to the given metric_name. It does not
61
+ # provide scoped measurements, meaning whatever is being traced will not 'blame the
62
+ # Controller'--that is to say appear in the breakdown chart.
63
+ # This is code is inlined in #add_method_tracer.
64
+ # * <tt>metric_names</tt> is a single name or an array of names of metrics
65
+ # * <tt>:force => true</tt> will force the metric to be captured even when
66
+ # tracing is disabled with NewRelic::Agent#disable_all_tracing
67
+ #
68
+ def trace_execution_unscoped(metric_names, options={})
69
+ return yield unless NewRelic::Agent.is_execution_traced?
70
+ t0 = Time.now
71
+ stats = Array(metric_names).map do | metric_name |
72
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope metric_name
73
+ end
74
+ begin
75
+ NewRelic::Agent.instance.push_trace_execution_flag(true) if options[:force]
76
+ yield
77
+ ensure
78
+ NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
79
+ duration = (Time.now - t0).to_f # for some reason this is 3 usec faster than Time - Time
80
+ stats.each { |stat| stat.trace_call(duration) }
81
+ end
82
+ end
83
+
84
+ EMPTY_ARRAY = [].freeze
85
+
86
+ # Deprecated. Use #trace_execution_scoped, a version with an options hash.
87
+ def trace_method_execution_with_scope(metric_names, produce_metric, deduct_call_time_from_parent, scoped_metric_only=false, &block) #:nodoc:
88
+ trace_execution_scoped(metric_names,
89
+ :metric => produce_metric,
90
+ :deduct_call_time_from_parent => deduct_call_time_from_parent,
91
+ :scoped_metric_only => scoped_metric_only, &block)
92
+ end
93
+
94
+ alias trace_method_execution_no_scope trace_execution_unscoped #:nodoc:
95
+
96
+ # Trace a given block with stats and keep track of the caller.
97
+ # See NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer for a description of the arguments.
98
+ # +metric_names+ is either a single name or an array of metric names.
99
+ # If more than one metric is passed, the +produce_metric+ option only applies to the first. The
100
+ # others are always recorded. Only the first metric is pushed onto the scope stack.
101
+ #
102
+ # Generally you pass an array of metric names if you want to record the metric under additional
103
+ # categories, but generally this *should never ever be done*. Most of the time you can aggregate
104
+ # on the server.
105
+
106
+ def trace_execution_scoped(metric_names, options={})
107
+
108
+ return yield unless NewRelic::Agent.is_execution_traced? || options[:force]
109
+
110
+ produce_metric = options[:metric] != false
111
+ deduct_call_time_from_parent = options[:deduct_call_time_from_parent] != false
112
+ scoped_metric_only = produce_metric && options[:scoped_metric_only]
113
+ t0 = Time.now
114
+ if metric_names.instance_of? Array
115
+ first_name = metric_names.first
116
+ metric_stats = []
117
+ metric_stats << NewRelic::Agent.instance.stats_engine.get_stats(first_name, true, scoped_metric_only) if produce_metric
118
+ metric_names[1..-1].each do | name |
119
+ metric_stats << NewRelic::Agent.instance.stats_engine.get_stats_no_scope(name)
120
+ end
121
+ else
122
+ first_name = metric_names
123
+ if produce_metric
124
+ metric_stats = [NewRelic::Agent.instance.stats_engine.get_stats(first_name, true, scoped_metric_only)]
125
+ else
126
+ metric_stats = EMPTY_ARRAY
127
+ end
128
+ end
129
+
130
+ begin
131
+ # Keep a reference to the scope we are pushing so we can do a sanity check making
132
+ # sure when we pop we get the one we 'expected'
133
+ NewRelic::Agent.instance.push_trace_execution_flag(true) if options[:force]
134
+ expected_scope = NewRelic::Agent.instance.stats_engine.push_scope(first_name, t0.to_f, deduct_call_time_from_parent)
135
+ rescue => e
136
+ NewRelic::Control.instance.log.error("Caught exception in trace_method_execution header. Metric name = #{first_name}, exception = #{e}")
137
+ NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
138
+ end
139
+
140
+ begin
141
+ yield
142
+ ensure
143
+ t1 = Time.now
144
+ duration = (t1 - t0).to_f
145
+
146
+ begin
147
+ NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
148
+ if expected_scope
149
+ scope = NewRelic::Agent.instance.stats_engine.pop_scope expected_scope, duration, t1.to_f
150
+ exclusive = duration - scope.children_time
151
+ metric_stats.each { |stats| stats.trace_call(duration, exclusive) }
152
+ end
153
+ rescue => e
154
+ NewRelic::Control.instance.log.error("Caught exception in trace_method_execution footer. Metric name = #{first_name}, exception = #{e}")
155
+ NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ module ClassMethods
162
+ # Add a method tracer to the specified method.
163
+ #
164
+ # === Common Options
165
+ #
166
+ # * <tt>:push_scope => false</tt> specifies this method tracer should not
167
+ # keep track of the caller; it will not show up in controller breakdown
168
+ # pie charts.
169
+ # * <tt>:metric => false</tt> specifies that no metric will be recorded.
170
+ # Instead the call will show up in transaction traces as well as traces
171
+ # shown in Developer Mode.
172
+ #
173
+ # === Uncommon Options
174
+ #
175
+ # * <tt>:scoped_metric_only => true</tt> indicates that the unscoped metric
176
+ # should not be recorded. Normally two metrics are potentially created
177
+ # on every invocation: the aggregate method where statistics for all calls
178
+ # of that metric are stored, and the "scoped metric" which records the
179
+ # statistics for invocations in a particular scope--generally a controller
180
+ # action. This option indicates that only the second type should be recorded.
181
+ # The effect is similar to <tt>:metric => false</tt> but in addition you
182
+ # will also see the invocation in breakdown pie charts.
183
+ # * <tt>:deduct_call_time_from_parent => false</tt> indicates that the method invocation
184
+ # time should never be deducted from the time reported as 'exclusive' in the
185
+ # caller. You would want to use this if you are tracing a recursive method
186
+ # or a method that might be called inside another traced method.
187
+ # * <tt>:code_header</tt> and <tt>:code_footer</tt> specify ruby code that
188
+ # is inserted into the tracer before and after the call.
189
+ # * <tt>:force = true</tt> will ensure the metric is captured even if called inside
190
+ # an untraced execution call. (See NewRelic::Agent#disable_all_tracing)
191
+ #
192
+ # === Overriding the metric name
193
+ #
194
+ # +metric_name_code+ is a string that is eval'd to get the
195
+ # name of the metric associated with the call, so if you want to
196
+ # use interpolaion evaluated at call time, then single quote
197
+ # the value like this:
198
+ #
199
+ # add_method_tracer :foo, 'Custom/#{self.class.name}/foo'
200
+ #
201
+ # This would name the metric according to the class of the runtime
202
+ # intance, as opposed to the class where +foo+ is defined.
203
+ #
204
+ # If not provided, the metric name will be <tt>Custom/ClassName/method_name</tt>.
205
+ #
206
+ # === Examples
207
+ #
208
+ # Instrument +foo+ only for custom views--will not show up in transaction traces or caller breakdown graphs:
209
+ #
210
+ # add_method_tracer :foo, :push_scope => false
211
+ #
212
+ # Instrument +foo+ just for transaction traces only:
213
+ #
214
+ # add_method_tracer :foo, :metric => false
215
+ #
216
+ # Instrument +foo+ so it shows up in transaction traces and caller breakdown graphs
217
+ # for actions:
218
+ #
219
+ # add_method_tracer :foo
220
+ #
221
+ # which is equivalent to:
222
+ #
223
+ # add_method_tracer :foo, 'Custom/#{self.class.name}/foo', :push_scope => true, :metric => true
224
+ #
225
+ # Instrument the class method +foo+ with the metric name 'Custom/People/fetch':
226
+ #
227
+ # class << self
228
+ # add_method_tracer :foo, 'Custom/People/fetch'
229
+ # end
230
+ #
231
+
232
+ def add_method_tracer(method_name, metric_name_code=nil, options = {})
233
+ # for backward compatibility:
234
+ if !options.is_a?(Hash)
235
+ options = {:push_scope => options}
236
+ end
237
+ # in case they omit the metric name code
238
+ if metric_name_code.is_a?(Hash)
239
+ options.merge(metric_name_code)
240
+ end
241
+ if (unrecognized = options.keys - [:force, :metric, :push_scope, :deduct_call_time_from_parent, :code_header, :code_footer, :scoped_metric_only]).any?
242
+ fail "Unrecognized options in add_method_tracer_call: #{unrecognized.join(', ')}"
243
+ end
244
+ # options[:push_scope] true if we are noting the scope of this for
245
+ # stats collection as well as the transaction tracing
246
+ options[:push_scope] = true if options[:push_scope].nil?
247
+ # options[:metric] true if you are tracking stats for a metric, otherwise
248
+ # it's just for transaction tracing.
249
+ options[:metric] = true if options[:metric].nil?
250
+ options[:force] = false if options[:force].nil?
251
+ options[:deduct_call_time_from_parent] = false if options[:deduct_call_time_from_parent].nil? && !options[:metric]
252
+ options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
253
+ options[:code_header] ||= ""
254
+ options[:code_footer] ||= ""
255
+ options[:scoped_metric_only] ||= false
256
+
257
+ klass = (self === Module) ? "self" : "self.class"
258
+ # Default to the class where the method is defined.
259
+ metric_name_code = "Custom/#{self.name}/#{method_name.to_s}" unless metric_name_code
260
+
261
+ unless method_defined?(method_name) || private_method_defined?(method_name)
262
+ NewRelic::Control.instance.log.warn("Did not trace #{self.name}##{method_name} because that method does not exist")
263
+ return
264
+ end
265
+
266
+ traced_method_name = _traced_method_name(method_name, metric_name_code)
267
+ if method_defined? traced_method_name
268
+ NewRelic::Control.instance.log.warn("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}")
269
+ return
270
+ end
271
+
272
+ fail "Can't add a tracer where push_scope is false and metric is false" if options[:push_scope] == false && !options[:metric]
273
+
274
+ header = ""
275
+ if !options[:force]
276
+ header << "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless NewRelic::Agent.is_execution_traced?\n"
277
+ end
278
+ header << options[:code_header] if options[:code_header]
279
+ if options[:push_scope] == false
280
+ code = <<-CODE
281
+ def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
282
+ #{header}
283
+ t0 = Time.now
284
+ stats = NewRelic::Agent.instance.stats_engine.get_stats_no_scope "#{metric_name_code}"
285
+ begin
286
+ #{"NewRelic::Agent.instance.push_trace_execution_flag(true)\n" if options[:force]}
287
+ #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)\n
288
+ ensure
289
+ #{"NewRelic::Agent.instance.pop_trace_execution_flag\n" if options[:force] }
290
+ duration = (Time.now - t0).to_f
291
+ stats.trace_call(duration)
292
+ #{options[:code_footer]}
293
+ end
294
+ end
295
+ CODE
296
+ else
297
+ code = <<-CODE
298
+ def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
299
+ #{options[:code_header]}
300
+ result = #{klass}.trace_execution_scoped("#{metric_name_code}",
301
+ :metric => #{options[:metric]},
302
+ :forced => #{options[:force]},
303
+ :deduct_call_time_from_parent => #{options[:deduct_call_time_from_parent]},
304
+ :scoped_metric_only => #{options[:scoped_metric_only]}) do
305
+ #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
306
+ end
307
+ #{options[:code_footer]}
308
+ result
309
+ end
310
+ CODE
311
+ end
312
+ class_eval code, __FILE__, __LINE__
313
+
314
+ alias_method _untraced_method_name(method_name, metric_name_code), method_name
315
+ alias_method method_name, _traced_method_name(method_name, metric_name_code)
316
+
317
+ NewRelic::Control.instance.log.debug("Traced method: class = #{self.name}, method = #{method_name}, "+
318
+ "metric = '#{metric_name_code}'")
319
+ end
320
+
321
+ # For tests only because tracers must be removed in reverse-order
322
+ # from when they were added, or else other tracers that were added to the same method
323
+ # may get removed as well.
324
+ def remove_method_tracer(method_name, metric_name_code) # :nodoc:
325
+ return unless NewRelic::Control.instance.agent_enabled?
326
+
327
+ if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
328
+ alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
329
+ undef_method "#{_traced_method_name(method_name, metric_name_code)}"
330
+ else
331
+ raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
332
+ end
333
+ end
334
+ private
335
+
336
+ def _untraced_method_name(method_name, metric_name)
337
+ "#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
338
+ end
339
+
340
+ def _traced_method_name(method_name, metric_name)
341
+ "#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
342
+ end
343
+
344
+ def _sanitize_name(name)
345
+ name.to_s.tr_s('^a-zA-Z0-9', '_')
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end