genki-newrelic_rpm 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/CHANGELOG +316 -0
  2. data/LICENSE +37 -0
  3. data/Manifest +156 -0
  4. data/README.md +138 -0
  5. data/Rakefile +22 -0
  6. data/bin/mongrel_rpm +33 -0
  7. data/bin/newrelic_cmd +4 -0
  8. data/cert/cacert.pem +34 -0
  9. data/genki-newrelic_rpm.gemspec +32 -0
  10. data/init.rb +38 -0
  11. data/install.rb +37 -0
  12. data/lib/new_relic/agent.rb +280 -0
  13. data/lib/new_relic/agent/agent.rb +627 -0
  14. data/lib/new_relic/agent/chained_call.rb +13 -0
  15. data/lib/new_relic/agent/collection_helper.rb +61 -0
  16. data/lib/new_relic/agent/error_collector.rb +125 -0
  17. data/lib/new_relic/agent/instrumentation/active_merchant.rb +18 -0
  18. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +83 -0
  19. data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
  20. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +368 -0
  21. data/lib/new_relic/agent/instrumentation/data_mapper.rb +90 -0
  22. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +132 -0
  23. data/lib/new_relic/agent/instrumentation/memcache.rb +21 -0
  24. data/lib/new_relic/agent/instrumentation/merb/controller.rb +26 -0
  25. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +13 -0
  26. data/lib/new_relic/agent/instrumentation/merb/errors.rb +8 -0
  27. data/lib/new_relic/agent/instrumentation/net.rb +12 -0
  28. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +20 -0
  29. data/lib/new_relic/agent/instrumentation/rack.rb +77 -0
  30. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +59 -0
  31. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
  32. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +38 -0
  33. data/lib/new_relic/agent/instrumentation/rails/errors.rb +27 -0
  34. data/lib/new_relic/agent/instrumentation/sinatra.rb +39 -0
  35. data/lib/new_relic/agent/method_tracer.rb +277 -0
  36. data/lib/new_relic/agent/patch_const_missing.rb +125 -0
  37. data/lib/new_relic/agent/sampler.rb +12 -0
  38. data/lib/new_relic/agent/samplers/cpu_sampler.rb +49 -0
  39. data/lib/new_relic/agent/samplers/memory_sampler.rb +137 -0
  40. data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
  41. data/lib/new_relic/agent/shim_agent.rb +21 -0
  42. data/lib/new_relic/agent/stats_engine.rb +24 -0
  43. data/lib/new_relic/agent/stats_engine/metric_stats.rb +111 -0
  44. data/lib/new_relic/agent/stats_engine/samplers.rb +71 -0
  45. data/lib/new_relic/agent/stats_engine/transactions.rb +155 -0
  46. data/lib/new_relic/agent/transaction_sampler.rb +319 -0
  47. data/lib/new_relic/agent/worker_loop.rb +118 -0
  48. data/lib/new_relic/commands/deployments.rb +145 -0
  49. data/lib/new_relic/commands/new_relic_commands.rb +30 -0
  50. data/lib/new_relic/control.rb +436 -0
  51. data/lib/new_relic/control/external.rb +13 -0
  52. data/lib/new_relic/control/merb.rb +22 -0
  53. data/lib/new_relic/control/rails.rb +143 -0
  54. data/lib/new_relic/control/ruby.rb +34 -0
  55. data/lib/new_relic/control/sinatra.rb +14 -0
  56. data/lib/new_relic/histogram.rb +89 -0
  57. data/lib/new_relic/local_environment.rb +285 -0
  58. data/lib/new_relic/merbtasks.rb +6 -0
  59. data/lib/new_relic/metric_data.rb +44 -0
  60. data/lib/new_relic/metric_parser.rb +120 -0
  61. data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
  62. data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
  63. data/lib/new_relic/metric_parser/active_record.rb +25 -0
  64. data/lib/new_relic/metric_parser/controller.rb +54 -0
  65. data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
  66. data/lib/new_relic/metric_parser/errors.rb +6 -0
  67. data/lib/new_relic/metric_parser/external.rb +50 -0
  68. data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
  69. data/lib/new_relic/metric_parser/view.rb +61 -0
  70. data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
  71. data/lib/new_relic/metric_parser/web_service.rb +9 -0
  72. data/lib/new_relic/metric_spec.rb +52 -0
  73. data/lib/new_relic/metrics.rb +7 -0
  74. data/lib/new_relic/noticed_error.rb +25 -0
  75. data/lib/new_relic/rack/metric_app.rb +56 -0
  76. data/lib/new_relic/rack/newrelic.ru +25 -0
  77. data/lib/new_relic/rack/newrelic.yml +25 -0
  78. data/lib/new_relic/rack_app.rb +5 -0
  79. data/lib/new_relic/recipes.rb +82 -0
  80. data/lib/new_relic/stats.rb +360 -0
  81. data/lib/new_relic/transaction_analysis.rb +121 -0
  82. data/lib/new_relic/transaction_sample.rb +583 -0
  83. data/lib/new_relic/version.rb +54 -0
  84. data/lib/new_relic_api.rb +315 -0
  85. data/lib/newrelic_rpm.rb +40 -0
  86. data/lib/tasks/all.rb +4 -0
  87. data/lib/tasks/install.rake +7 -0
  88. data/lib/tasks/tests.rake +13 -0
  89. data/newrelic.yml +214 -0
  90. data/recipes/newrelic.rb +6 -0
  91. data/test/active_record_fixtures.rb +55 -0
  92. data/test/config/newrelic.yml +46 -0
  93. data/test/config/test_control.rb +39 -0
  94. data/test/new_relic/agent/active_record_instrumentation_test.rb +234 -0
  95. data/test/new_relic/agent/agent_controller_test.rb +107 -0
  96. data/test/new_relic/agent/agent_test.rb +117 -0
  97. data/test/new_relic/agent/agent_test_controller.rb +44 -0
  98. data/test/new_relic/agent/classloader_patch_test.rb +56 -0
  99. data/test/new_relic/agent/collection_helper_test.rb +118 -0
  100. data/test/new_relic/agent/dispatcher_instrumentation_test.rb +76 -0
  101. data/test/new_relic/agent/error_collector_test.rb +155 -0
  102. data/test/new_relic/agent/method_tracer_test.rb +335 -0
  103. data/test/new_relic/agent/metric_data_test.rb +56 -0
  104. data/test/new_relic/agent/mock_ar_connection.rb +40 -0
  105. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  106. data/test/new_relic/agent/net_instrumentation_test.rb +51 -0
  107. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
  108. data/test/new_relic/agent/stats_engine/samplers_test.rb +78 -0
  109. data/test/new_relic/agent/stats_engine/stats_engine_test.rb +177 -0
  110. data/test/new_relic/agent/task_instrumentation_test.rb +67 -0
  111. data/test/new_relic/agent/testable_agent.rb +13 -0
  112. data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
  113. data/test/new_relic/agent/transaction_sample_test.rb +146 -0
  114. data/test/new_relic/agent/transaction_sampler_test.rb +387 -0
  115. data/test/new_relic/agent/worker_loop_test.rb +103 -0
  116. data/test/new_relic/control_test.rb +94 -0
  117. data/test/new_relic/deployments_api_test.rb +68 -0
  118. data/test/new_relic/environment_test.rb +75 -0
  119. data/test/new_relic/metric_parser_test.rb +172 -0
  120. data/test/new_relic/metric_spec_test.rb +177 -0
  121. data/test/new_relic/shim_agent_test.rb +9 -0
  122. data/test/new_relic/stats_test.rb +291 -0
  123. data/test/new_relic/version_number_test.rb +74 -0
  124. data/test/test_helper.rb +38 -0
  125. data/test/ui/newrelic_controller_test.rb +14 -0
  126. data/test/ui/newrelic_helper_test.rb +53 -0
  127. data/ui/controllers/newrelic_controller.rb +214 -0
  128. data/ui/helpers/google_pie_chart.rb +55 -0
  129. data/ui/helpers/newrelic_helper.rb +314 -0
  130. data/ui/views/layouts/newrelic_default.rhtml +47 -0
  131. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  132. data/ui/views/newrelic/_sample.rhtml +15 -0
  133. data/ui/views/newrelic/_segment.rhtml +28 -0
  134. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  135. data/ui/views/newrelic/_segment_row.rhtml +14 -0
  136. data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
  137. data/ui/views/newrelic/_show_sample_sql.rhtml +20 -0
  138. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  139. data/ui/views/newrelic/_sql_row.rhtml +11 -0
  140. data/ui/views/newrelic/_stack_trace.rhtml +30 -0
  141. data/ui/views/newrelic/_table.rhtml +12 -0
  142. data/ui/views/newrelic/explain_sql.rhtml +42 -0
  143. data/ui/views/newrelic/images/arrow-close.png +0 -0
  144. data/ui/views/newrelic/images/arrow-open.png +0 -0
  145. data/ui/views/newrelic/images/blue_bar.gif +0 -0
  146. data/ui/views/newrelic/images/file_icon.png +0 -0
  147. data/ui/views/newrelic/images/gray_bar.gif +0 -0
  148. data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
  149. data/ui/views/newrelic/images/textmate.png +0 -0
  150. data/ui/views/newrelic/index.rhtml +45 -0
  151. data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
  152. data/ui/views/newrelic/javascript/transaction_sample.js +107 -0
  153. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  154. data/ui/views/newrelic/show_sample.rhtml +77 -0
  155. data/ui/views/newrelic/show_source.rhtml +3 -0
  156. data/ui/views/newrelic/stylesheets/style.css +433 -0
  157. data/ui/views/newrelic/threads.rhtml +52 -0
  158. metadata +327 -0
@@ -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,38 @@
1
+ require 'dispatcher'
2
+ # NewRelic RPM instrumentation for http request dispatching (Routes mapping)
3
+ # Note, the dispatcher class from no module into into the ActionController module
4
+ # in Rails 2.0. Thus we need to check for both
5
+ if defined? ActionController::Dispatcher
6
+ target = ActionController::Dispatcher
7
+ elsif defined? Dispatcher
8
+ target = Dispatcher
9
+ end
10
+
11
+ # NOTE TODO: maybe this should be done with a middleware?
12
+ if target
13
+ require 'action_pack/version'
14
+ NewRelic::Agent.instance.log.debug "Adding #{target} instrumentation"
15
+
16
+ target.class_eval do
17
+ if ActionPack::VERSION::MAJOR >= 2
18
+ # In versions later that 1.* the dispatcher callbacks are used
19
+ include NewRelic::Agent::Instrumentation::DispatcherInstrumentation
20
+ before_dispatch :newrelic_dispatcher_start
21
+ after_dispatch :newrelic_dispatcher_finish
22
+ def newrelic_response_code
23
+ (@response.headers['Status']||'200')[0..2] if @response && ActionPack::VERSION::MAJOR == 2 && ActionPack::VERSION::MINOR < 3
24
+ end
25
+ else
26
+ # In version 1.2.* the instrumentation is done by method chaining
27
+ # the static dispatch method on the dispatcher class
28
+ extend NewRelic::Agent::Instrumentation::DispatcherInstrumentation
29
+ class << self
30
+ alias_method :dispatch_without_newrelic, :dispatch
31
+ alias_method :dispatch, :dispatch_newrelic
32
+ def newrelic_response_code; end
33
+ end
34
+ end
35
+ end
36
+ else
37
+ NewRelic::Agent.instance.log.debug "WARNING: Dispatcher instrumentation not added"
38
+ end
@@ -0,0 +1,27 @@
1
+
2
+ ActionController::Base.class_eval do
3
+
4
+ # Make a note of an exception associated with the currently executin
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
+ filtered_params = (respond_to? :filter_parameters) ? filter_parameters(params) : params
10
+ filtered_params.merge!(custom_params)
11
+ NewRelic::Agent.agent.error_collector.notice_error(exception, request, newrelic_metric_path, filtered_params)
12
+ end
13
+
14
+ def rescue_action_with_newrelic_trace(exception)
15
+ newrelic_notice_error exception
16
+
17
+ rescue_action_without_newrelic_trace exception
18
+ end
19
+
20
+ # Compare with #alias_method_chain, which is not available in
21
+ # Rails 1.1:
22
+ alias_method :rescue_action_without_newrelic_trace, :rescue_action
23
+ alias_method :rescue_action, :rescue_action_with_newrelic_trace
24
+ protected :rescue_action
25
+
26
+ end if defined? ActionController
27
+
@@ -0,0 +1,39 @@
1
+ module NewRelic::Agent::Instrumentation
2
+ # NewRelic instrumentation for Sinatra applications. Sinatra actions will
3
+ # appear in the UI similar to controller actions, and have breakdown charts
4
+ # and transaction traces.
5
+ #
6
+ # The actions in the UI will correspond to the pattern expression used
7
+ # to match them. HTTP operations are not distinguished. Multiple matches
8
+ # will all be tracked as separate actions.
9
+ module Sinatra
10
+
11
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
12
+
13
+ def route_eval_with_newrelic(&block_arg)
14
+ path = unescape(@request.path_info)
15
+ name = path
16
+ # Go through each route and look for a match
17
+ if routes = self.class.routes[@request.request_method]
18
+ routes.detect do |pattern, keys, conditions, block|
19
+ if block_arg.equal? block
20
+ name = pattern.source
21
+ end
22
+ end
23
+ end
24
+ # strip of leading ^ and / chars and trailing $ and /
25
+ name.gsub!(%r{^[/^]*(.*?)[/\$]*$}, '\1')
26
+ name = 'root' if name.empty?
27
+ perform_action_with_newrelic_trace(:category => :sinatra, :name => name) do
28
+ route_eval_without_newrelic(&block_arg)
29
+ end
30
+ end
31
+ end
32
+
33
+ Sinatra::Base.class_eval do
34
+ include NewRelic::Agent::Instrumentation::Sinatra
35
+ alias route_eval_without_newrelic route_eval
36
+ alias route_eval route_eval_with_newrelic
37
+ end
38
+
39
+ end if defined?(Sinatra::Base)
@@ -0,0 +1,277 @@
1
+ module NewRelic::Agent
2
+
3
+ # These are the class methods added to support installing custom
4
+ # metric tracers and executing for individual metrics.
5
+ # This module is included in class Module.
6
+ module MethodTracer
7
+
8
+ # Deprecated: original method preserved for API backward compatibility.
9
+ # Use either #trace_execution_scoped or #trace_execution_unscoped
10
+ def trace_method_execution(metric_names, push_scope, produce_metric, deduct_call_time_from_parent, &block) #:nodoc:
11
+ if push_scope
12
+ trace_execution_scoped(metric_names, :metric => produce_metric,
13
+ :deduct_call_time_from_parent => deduct_call_time_from_parent, &block)
14
+ else
15
+ trace_execution_unscoped(metric_names, &block)
16
+ end
17
+ end
18
+
19
+ # Trace a given block with stats assigned to the given metric_name. It does not
20
+ # provide scoped measurements, meaning whatever is being traced will not 'blame the
21
+ # Controller'--that is to say appear in the breakdown chart.
22
+ # This is code is inlined in #add_method_tracer.
23
+ # * <tt>metric_names</tt> is a single name or an array of names of metrics
24
+ # * <tt>:force => true</tt> will force the metric to be captured even when
25
+ # tracing is disabled with NewRelic::Agent#disable_all_tracing
26
+ #
27
+ def trace_execution_unscoped(metric_names, options={})
28
+ return yield unless NewRelic::Agent.is_execution_traced?
29
+ t0 = Time.now.to_f
30
+ stats = Array(metric_names).map do | metric_name |
31
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope metric_name
32
+ end
33
+ begin
34
+ NewRelic::Agent.instance.push_trace_execution_flag(true) if options[:force]
35
+ yield
36
+ ensure
37
+ NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
38
+ duration = Time.now.to_f - t0 # for some reason this is 3 usec faster than Time - Time
39
+ stats.each { |stat| stat.trace_call(duration) }
40
+ end
41
+ end
42
+
43
+ EMPTY_ARRAY = [].freeze #:nodoc:
44
+
45
+ # Deprecated. Use #trace_execution_scoped, a version with an options hash.
46
+ def trace_method_execution_with_scope(metric_names, produce_metric, deduct_call_time_from_parent, scoped_metric_only=false) #:nodoc
47
+ trace_execution_scoped(metric_names,
48
+ :metric => produce_metric,
49
+ :deduct_call_time_from_parent => deduct_call_time_from_parent,
50
+ :scoped_metric_only => scoped_metric_only)
51
+ end
52
+
53
+ alias trace_method_execution_no_scope trace_execution_unscoped
54
+
55
+ # Trace a given block with stats and keep track of the caller.
56
+ # See #add_method_tracer for a description of the arguments.
57
+ # +metric_names+ is either a single name or an array of metric names.
58
+ # If more than one metric is passed, the +produce_metric+ option only applies to the first. The
59
+ # others are always recorded. Only the first metric is pushed onto the scope stack.
60
+ #
61
+ # Generally you pass an array of metric names if you want to record the metric under additional
62
+ # categories, but generally this *should never ever be done*. Most of the time you can aggregate
63
+ # on the server.
64
+
65
+ def trace_execution_scoped(metric_names, options={})
66
+
67
+ return yield unless NewRelic::Agent.is_execution_traced? || options[:force]
68
+
69
+ produce_metric = options[:metric] != false
70
+ deduct_call_time_from_parent = options[:deduct_call_time_from_parent] != false
71
+ scoped_metric_only = options[:scoped_metric_only]
72
+ t0 = Time.now.to_f
73
+ if metric_names.instance_of? Array
74
+ first_name = metric_names.first
75
+ metric_stats = []
76
+ metric_stats << NewRelic::Agent.instance.stats_engine.get_stats(first_name, true, scoped_metric_only) if produce_metric
77
+ metric_names[1..-1].each do | name |
78
+ metric_stats << NewRelic::Agent.instance.stats_engine.get_stats_no_scope(name)
79
+ end
80
+ else
81
+ first_name = metric_names
82
+ if produce_metric
83
+ metric_stats = [NewRelic::Agent.instance.stats_engine.get_stats(first_name, true, scoped_metric_only)]
84
+ else
85
+ metric_stats = EMPTY_ARRAY
86
+ end
87
+ end
88
+
89
+ begin
90
+ # Keep a reference to the scope we are pushing so we can do a sanity check making
91
+ # sure when we pop we get the one we 'expected'
92
+ NewRelic::Agent.instance.push_trace_execution_flag(true) if options[:force]
93
+ expected_scope = NewRelic::Agent.instance.stats_engine.push_scope(first_name, t0, deduct_call_time_from_parent)
94
+ rescue => e
95
+ NewRelic::Control.instance.log.error("Caught exception in trace_method_execution header. Metric name = #{first_name}, exception = #{e}")
96
+ NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
97
+ end
98
+
99
+ begin
100
+ yield
101
+ ensure
102
+ t1 = Time.now.to_f
103
+ duration = t1 - t0
104
+
105
+ begin
106
+ NewRelic::Agent.instance.pop_trace_execution_flag if options[:force]
107
+ if expected_scope
108
+ scope = NewRelic::Agent.instance.stats_engine.pop_scope expected_scope, duration, t1
109
+ exclusive = duration - scope.children_time
110
+ metric_stats.each { |stats| stats.trace_call(duration, exclusive) }
111
+ end
112
+ rescue => e
113
+ NewRelic::Control.instance.log.error("Caught exception in trace_method_execution footer. Metric name = #{first_name}, exception = #{e}")
114
+ NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
115
+ end
116
+ end
117
+ end
118
+
119
+ # Add a method tracer to the specified method.
120
+ #
121
+ # === Common Options
122
+ #
123
+ # * <tt>:push_scope => false</tt> specifies this method tracer should not
124
+ # keep track of the caller; it will show up in controller breakdown
125
+ # pie charts.
126
+ # * <tt>:metric => false</tt> specifies that no metric will be recorded.
127
+ # Instead the call will show up in transaction traces as well as traces
128
+ # shown in Developer Mode.
129
+ #
130
+ # === Uncommon Options
131
+ #
132
+ # * <tt>:scoped_metric_only => true</tt> indicates that the unscoped metric
133
+ # should not be recorded. Normally two metrics are potentially created
134
+ # on every invocation: the aggregate method where statistics for all calls
135
+ # of that metric are stored, and the "scoped metric" which records the
136
+ # statistics for invocations in a particular scope--generally a controller
137
+ # action. This option indicates that only the second type should be recorded.
138
+ # The effect is similar to <tt>:metric => false</tt> but in addition you
139
+ # will also see the invocation in breakdown pie charts.
140
+ # * <tt>:deduct_call_time_from_parent => false</tt> indicates that the method invocation
141
+ # time should never be deducted from the time reported as 'exclusive' in the
142
+ # caller. You would want to use this if you are tracing a recursive method
143
+ # or a method that might be called inside another traced method.
144
+ # * <tt>:code_header</tt> and <tt>:code_footer</tt> specify ruby code that
145
+ # is inserted into the tracer before and after the call.
146
+ # * <tt>:force = true</tt> will ensure the metric is captured even if called inside
147
+ # an untraced execution call. (See NewRelic::Agent#disable_all_tracing)
148
+ #
149
+ # === Overriding the metric name
150
+ #
151
+ # +metric_name_code+ is a string that is eval'd to get the
152
+ # name of the metric associated with the call, so if you want to
153
+ # use interpolaion evaluated at call time, then single quote
154
+ # the value like this:
155
+ #
156
+ # add_method_tracer :foo, 'Custom/#{self.class.name}/foo'
157
+ #
158
+ # This would name the metric according to the class of the runtime
159
+ # intance, as opposed to the class where +foo+ is defined.
160
+ #
161
+ # If not provided, the metric name will be <tt>Custom/ClassName/method_name</tt>.
162
+
163
+ def add_method_tracer(method_name, metric_name_code=nil, options = {})
164
+ # for backward compatibility:
165
+ if !options.is_a?(Hash)
166
+ options = {:push_scope => options}
167
+ end
168
+ if (unrecognized = options.keys - [:force, :metric, :push_scope, :deduct_call_time_from_parent, :code_header, :code_footer, :scoped_metric_only]).any?
169
+ fail "Unrecognized options in add_method_tracer_call: #{unrecognized.join(', ')}"
170
+ end
171
+ # options[:push_scope] true if we are noting the scope of this for
172
+ # stats collection as well as the transaction tracing
173
+ options[:push_scope] = true if options[:push_scope].nil?
174
+ # options[:metric] true if you are tracking stats for a metric, otherwise
175
+ # it's just for transaction tracing.
176
+ options[:metric] = true if options[:metric].nil?
177
+ options[:force] = false if options[:force].nil?
178
+ options[:deduct_call_time_from_parent] = false if options[:deduct_call_time_from_parent].nil? && !options[:metric]
179
+ options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
180
+ options[:code_header] ||= ""
181
+ options[:code_footer] ||= ""
182
+ options[:scoped_metric_only] ||= false
183
+
184
+ klass = (self === Module) ? "self" : "self.class"
185
+ # Default to the class where the method is defined.
186
+ metric_name_code = "Custom/#{self.name}/#{method_name.to_s}" unless metric_name_code
187
+
188
+ unless method_defined?(method_name) || private_method_defined?(method_name)
189
+ NewRelic::Control.instance.log.warn("Did not trace #{self.name}##{method_name} because that method does not exist")
190
+ return
191
+ end
192
+
193
+ traced_method_name = _traced_method_name(method_name, metric_name_code)
194
+ if method_defined? traced_method_name
195
+ NewRelic::Control.instance.log.warn("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}")
196
+ return
197
+ end
198
+
199
+ fail "Can't add a tracer where push_scope is false and metric is false" if options[:push_scope] == false && !options[:metric]
200
+
201
+ header = ""
202
+ if !options[:force]
203
+ header << "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless NewRelic::Agent.is_execution_traced?\n"
204
+ end
205
+ header << options[:code_header] if options[:code_header]
206
+ if options[:push_scope] == false
207
+ code = <<-CODE
208
+ def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
209
+ #{header}
210
+ t0 = Time.now.to_f
211
+ stats = NewRelic::Agent.instance.stats_engine.get_stats_no_scope "#{metric_name_code}"
212
+ begin
213
+ #{"NewRelic::Agent.instance.push_trace_execution_flag(true)\n" if options[:force]}
214
+ #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)\n
215
+ ensure
216
+ #{"NewRelic::Agent.instance.pop_trace_execution_flag\n" if options[:force] }
217
+ duration = Time.now.to_f - t0
218
+ stats.trace_call(duration)
219
+ #{options[:code_footer]}
220
+ end
221
+ end
222
+ CODE
223
+ else
224
+ code = <<-CODE
225
+ def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
226
+ #{options[:code_header]}
227
+ result = #{klass}.trace_execution_scoped("#{metric_name_code}",
228
+ :metric => #{options[:metric]},
229
+ :forced => #{options[:force]},
230
+ :deduct_call_time_from_parent => #{options[:deduct_call_time_from_parent]},
231
+ :scoped_metric_only => #{options[:scoped_metric_only]}) do
232
+ #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
233
+ end
234
+ #{options[:code_footer]}
235
+ result
236
+ end
237
+ CODE
238
+ end
239
+
240
+ class_eval code, __FILE__, __LINE__
241
+
242
+ alias_method _untraced_method_name(method_name, metric_name_code), method_name
243
+ alias_method method_name, _traced_method_name(method_name, metric_name_code)
244
+
245
+ NewRelic::Control.instance.log.debug("Traced method: class = #{self.name}, method = #{method_name}, "+
246
+ "metric = '#{metric_name_code}', options: #{options.inspect}, ")
247
+ end
248
+
249
+ # Not recommended for production use, because tracers must be removed in reverse-order
250
+ # from when they were added, or else other tracers that were added to the same method
251
+ # may get removed as well.
252
+ def remove_method_tracer(method_name, metric_name_code)
253
+ return unless NewRelic::Control.instance.agent_enabled?
254
+
255
+ if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
256
+ alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
257
+ undef_method "#{_traced_method_name(method_name, metric_name_code)}"
258
+ else
259
+ raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
260
+ end
261
+ end
262
+
263
+ private
264
+
265
+ def _untraced_method_name(method_name, metric_name)
266
+ "#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
267
+ end
268
+
269
+ def _traced_method_name(method_name, metric_name)
270
+ "#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
271
+ end
272
+
273
+ def _sanitize_name(name)
274
+ name.to_s.tr('^a-z,A-Z,0-9', '_')
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,125 @@
1
+ # This class is for debugging purposes only.
2
+ # It inserts instrumentation into class loading to verify
3
+ # that no classes are being loaded on the new relic thread,
4
+ # which can cause problems in the class loader code.
5
+ # It is only loaded by agent.rb when a particular newrelic.yml
6
+ # option is set.
7
+
8
+ module ClassLoadingWatcher # :nodoc: all
9
+
10
+ extend self
11
+ @@background_thread = nil
12
+ @@flag_const_missing = nil
13
+
14
+ def background_thread
15
+ @@background_thread
16
+ end
17
+ def flag_const_missing
18
+ @@flag_const_missing
19
+ end
20
+ def flag_const_missing=(val)
21
+ @@flag_const_missing = val
22
+ end
23
+
24
+ def background_thread=(thread)
25
+ @@background_thread = thread
26
+
27
+ # these tests verify that check is working
28
+ =begin
29
+ @@background_thread = nil
30
+ bad = ConstMissingInForegroundThread rescue nil
31
+ @@background_thread = thread
32
+ bad = ConstMissingInBackgroundThread rescue nil
33
+ require 'new_relic/agent/patch_const_missing'
34
+ load 'new_relic/agent/patch_const_missing.rb'
35
+ =end
36
+ end
37
+ module SanityCheck
38
+ def nr_check_for_classloading(*args)
39
+
40
+ if Thread.current == ClassLoadingWatcher.background_thread
41
+ nr_error "Agent background thread shouldn't be loading classes (#{args.inspect})"
42
+ end
43
+ end
44
+ #
45
+ def nr_check_for_constmissing(*args)
46
+ if ClassLoadingWatcher.flag_const_missing
47
+ nr_error "Classes in Agent should not be loaded via const_missing (#{args.inspect})"
48
+ end
49
+ end
50
+ private
51
+ def nr_error(msg)
52
+ exception = NewRelic::Agent::BackgroundLoadingError.new(msg)
53
+ backtrace = caller
54
+ backtrace.shift
55
+ exception.set_backtrace(backtrace)
56
+ NewRelic::Agent.instance.error_collector.notice_error(exception, nil)
57
+ msg << "\n" << backtrace.join("\n")
58
+ NewRelic::Control.instance.log.error msg
59
+ end
60
+ end
61
+ def enable_warning
62
+ Object.class_eval do
63
+ if !defined?(non_new_relic_require)
64
+ alias_method :non_new_relic_require, :require
65
+ alias_method :require, :new_relic_require
66
+ end
67
+
68
+ if !defined?(non_new_relic_load)
69
+ alias_method :non_new_relic_load, :load
70
+ alias_method :load, :new_relic_load
71
+ end
72
+ end
73
+ Module.class_eval do
74
+ if !defined?(non_new_relic_const_missing)
75
+ alias_method :non_new_relic_const_missing, :const_missing
76
+ alias_method :const_missing, :new_relic_const_missing
77
+ end
78
+ end
79
+ end
80
+
81
+ def disable_warning
82
+ Object.class_eval do
83
+ if defined?(non_new_relic_require)
84
+ alias_method :require, :non_new_relic_require
85
+ undef non_new_relic_require
86
+ end
87
+
88
+ if defined?(non_new_relic_load)
89
+ alias_method :load, :non_new_relic_load
90
+ undef non_new_relic_load
91
+ end
92
+ end
93
+ Module.class_eval do
94
+ if defined?(non_new_relic_const_missing)
95
+ alias_method :const_missing, :non_new_relic_const_missing
96
+ undef non_new_relic_const_missing
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ class Object # :nodoc:
103
+ include ClassLoadingWatcher::SanityCheck
104
+
105
+ def new_relic_require(*args)
106
+ nr_check_for_classloading("Object require", *args)
107
+ non_new_relic_require(*args)
108
+ end
109
+
110
+ def new_relic_load(*args)
111
+ nr_check_for_classloading("Object load", *args)
112
+ non_new_relic_load(*args)
113
+ end
114
+ end
115
+
116
+
117
+ class Module # :nodoc:
118
+ include ClassLoadingWatcher::SanityCheck
119
+
120
+ def new_relic_const_missing(*args)
121
+ nr_check_for_constmissing("Module #{self.name} const_missing", *args)
122
+ nr_check_for_classloading("Module #{self.name} const_missing", *args)
123
+ non_new_relic_const_missing(*args)
124
+ end
125
+ end