newrelic_rpm 2.13.0.beta5 → 2.13.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (86) hide show
  1. data/CHANGELOG +4 -0
  2. data/lib/new_relic/agent.rb +50 -50
  3. data/lib/new_relic/agent/agent.rb +24 -19
  4. data/lib/new_relic/agent/busy_calculator.rb +22 -22
  5. data/lib/new_relic/agent/chained_call.rb +3 -3
  6. data/lib/new_relic/agent/error_collector.rb +19 -19
  7. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +11 -11
  8. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +2 -2
  9. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +43 -43
  10. data/lib/new_relic/agent/instrumentation/data_mapper.rb +6 -6
  11. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +2 -2
  12. data/lib/new_relic/agent/instrumentation/memcache.rb +8 -8
  13. data/lib/new_relic/agent/instrumentation/merb/controller.rb +4 -4
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +307 -303
  15. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +8 -8
  16. data/lib/new_relic/agent/instrumentation/rack.rb +2 -2
  17. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +10 -10
  18. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +3 -3
  19. data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -5
  20. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +5 -5
  21. data/lib/new_relic/agent/instrumentation/sinatra.rb +5 -5
  22. data/lib/new_relic/agent/instrumentation/sunspot.rb +1 -1
  23. data/lib/new_relic/agent/method_tracer.rb +55 -55
  24. data/lib/new_relic/agent/sampler.rb +42 -38
  25. data/lib/new_relic/agent/samplers/cpu_sampler.rb +4 -4
  26. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +7 -7
  27. data/lib/new_relic/agent/samplers/memory_sampler.rb +11 -11
  28. data/lib/new_relic/agent/samplers/object_sampler.rb +1 -1
  29. data/lib/new_relic/agent/shim_agent.rb +20 -16
  30. data/lib/new_relic/agent/stats_engine.rb +3 -3
  31. data/lib/new_relic/agent/stats_engine/metric_stats.rb +28 -28
  32. data/lib/new_relic/agent/stats_engine/samplers.rb +16 -16
  33. data/lib/new_relic/agent/stats_engine/transactions.rb +25 -25
  34. data/lib/new_relic/agent/transaction_sampler.rb +68 -69
  35. data/lib/new_relic/agent/worker_loop.rb +13 -13
  36. data/lib/new_relic/collection_helper.rb +6 -6
  37. data/lib/new_relic/command.rb +14 -14
  38. data/lib/new_relic/commands/deployments.rb +19 -19
  39. data/lib/new_relic/commands/install.rb +25 -15
  40. data/lib/new_relic/control.rb +25 -25
  41. data/lib/new_relic/control/configuration.rb +17 -17
  42. data/lib/new_relic/control/frameworks/external.rb +3 -3
  43. data/lib/new_relic/control/frameworks/merb.rb +6 -6
  44. data/lib/new_relic/control/frameworks/rails.rb +17 -17
  45. data/lib/new_relic/control/frameworks/rails3.rb +11 -27
  46. data/lib/new_relic/control/frameworks/ruby.rb +6 -6
  47. data/lib/new_relic/control/frameworks/sinatra.rb +4 -4
  48. data/lib/new_relic/control/instrumentation.rb +8 -8
  49. data/lib/new_relic/control/logging_methods.rb +13 -13
  50. data/lib/new_relic/control/profiling.rb +2 -2
  51. data/lib/new_relic/control/server_methods.rb +17 -17
  52. data/lib/new_relic/delayed_job_injection.rb +1 -1
  53. data/lib/new_relic/histogram.rb +73 -71
  54. data/lib/new_relic/local_environment.rb +45 -45
  55. data/lib/new_relic/merbtasks.rb +1 -1
  56. data/lib/new_relic/metric_data.rb +5 -5
  57. data/lib/new_relic/metric_parser.rb +22 -22
  58. data/lib/new_relic/metric_parser/action_mailer.rb +4 -4
  59. data/lib/new_relic/metric_parser/active_merchant.rb +8 -8
  60. data/lib/new_relic/metric_parser/active_record.rb +2 -2
  61. data/lib/new_relic/metric_parser/apdex.rb +86 -51
  62. data/lib/new_relic/metric_parser/controller.rb +10 -10
  63. data/lib/new_relic/metric_parser/controller_cpu.rb +5 -5
  64. data/lib/new_relic/metric_parser/errors.rb +1 -1
  65. data/lib/new_relic/metric_parser/external.rb +3 -3
  66. data/lib/new_relic/metric_parser/mem_cache.rb +2 -2
  67. data/lib/new_relic/metric_parser/other_transaction.rb +7 -7
  68. data/lib/new_relic/metric_parser/view.rb +5 -5
  69. data/lib/new_relic/metric_parser/web_frontend.rb +1 -1
  70. data/lib/new_relic/metric_parser/web_service.rb +1 -1
  71. data/lib/new_relic/metric_spec.rb +13 -13
  72. data/lib/new_relic/noticed_error.rb +4 -4
  73. data/lib/new_relic/rack/developer_mode.rb +33 -33
  74. data/lib/new_relic/rack/metric_app.rb +2 -2
  75. data/lib/new_relic/recipes.rb +9 -9
  76. data/lib/new_relic/stats.rb +57 -57
  77. data/lib/new_relic/timer_lib.rb +2 -2
  78. data/lib/new_relic/transaction_analysis.rb +19 -19
  79. data/lib/new_relic/transaction_sample.rb +101 -101
  80. data/lib/new_relic/url_rule.rb +3 -3
  81. data/lib/new_relic/version.rb +10 -10
  82. data/lib/newrelic_rpm.rb +6 -4
  83. data/lib/tasks/all.rb +1 -1
  84. data/newrelic_rpm.gemspec +3 -3
  85. data/test/new_relic/rack/episodes_test.rb +1 -0
  86. metadata +24 -42
@@ -2,7 +2,7 @@
2
2
  # NewRelic instrumentation for DataMapper
3
3
  # For now, we have to refer to all db metrics as "ActiveRecord"
4
4
  if defined? DataMapper
5
-
5
+
6
6
  DataMapper::Model.class_eval do
7
7
  add_method_tracer :get, 'ActiveRecord/#{self.name}/find'
8
8
  add_method_tracer :first, 'ActiveRecord/#{self.name}/find'
@@ -37,7 +37,7 @@ if defined? DataMapper
37
37
  log_with_capture_sql(sql, name, &block)
38
38
  end
39
39
  end
40
-
40
+
41
41
  def log_with_capture_sql(sql, name, &block)
42
42
  if @@my_sql_defined && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
43
43
  config = @config
@@ -46,13 +46,13 @@ if defined? DataMapper
46
46
  else
47
47
  config = nil
48
48
  end
49
-
49
+
50
50
  t0 = Time.now
51
51
  result = log_without_newrelic_instrumentation(sql, name, &block)
52
-
52
+
53
53
  NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, config, (Time.now - t0).to_f)
54
-
54
+
55
55
  result
56
56
  end
57
57
  end
58
- end
58
+ end
@@ -14,8 +14,8 @@ if defined?(Delayed::Job) and not NewRelic::Control.instance['disable_dj']
14
14
  add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob'
15
15
  end
16
16
  end
17
-
18
- end
17
+
18
+ end
19
19
  end
20
20
  end
21
21
  end
@@ -1,15 +1,15 @@
1
1
  # NOTE there are multiple implementations of the MemCache client in Ruby,
2
- # each with slightly different API's and semantics.
2
+ # each with slightly different API's and semantics.
3
3
  # See:
4
4
  # http://www.deveiate.org/code/Ruby-MemCache/ (Gem: Ruby-MemCache)
5
5
  # http://seattlerb.rubyforge.org/memcache-client/ (Gem: memcache-client)
6
6
  unless NewRelic::Control.instance['disable_memcache_instrumentation']
7
-
7
+
8
8
  def self.instrument_method(the_class, method_name)
9
9
  return unless the_class.method_defined? method_name.to_sym
10
10
  the_class.class_eval <<-EOD
11
11
  def #{method_name}_with_newrelic_trace(*args)
12
- metrics = ["MemCache/#{method_name}",
12
+ metrics = ["MemCache/#{method_name}",
13
13
  (NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction? ? 'MemCache/allWeb' : 'MemCache/allOther')]
14
14
  self.class.trace_execution_scoped(metrics) do
15
15
  t0 = Time.now
@@ -26,15 +26,15 @@ unless NewRelic::Control.instance['disable_memcache_instrumentation']
26
26
  end
27
27
 
28
28
  def self.memcache_key_snippet(method_name)
29
- return "" unless NewRelic::Control.instance['capture_memcache_keys']
29
+ return "" unless NewRelic::Control.instance['capture_memcache_keys']
30
30
  "NewRelic::Agent.instance.transaction_sampler.notice_nosql(args.first.inspect, (Time.now - t0).to_f) rescue nil"
31
31
  end
32
-
33
-
32
+
33
+
34
34
 
35
35
  %w[get get_multi set add incr decr delete replace append prepand cas].each do | method_name |
36
- instrument_method(::MemCache, method_name) if defined? ::MemCache
37
- instrument_method(::Memcached, method_name) if defined? ::Memcached
36
+ instrument_method(::MemCache, method_name) if defined? ::MemCache
37
+ instrument_method(::Memcached, method_name) if defined? ::Memcached
38
38
  end
39
39
 
40
40
  end
@@ -3,18 +3,18 @@ require 'merb-core/controller/merb_controller'
3
3
 
4
4
  Merb::Controller.class_eval do
5
5
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
6
-
6
+
7
7
  class_inheritable_accessor :do_not_trace
8
8
  class_inheritable_accessor :ignore_apdex
9
-
9
+
10
10
  def self.newrelic_write_attr(attr_name, value) # :nodoc:
11
11
  self.send "#{attr_name}=", attr_name, value
12
12
  end
13
-
13
+
14
14
  def self.newrelic_read_attr(attr_name) # :nodoc:
15
15
  self.send attr_name
16
16
  end
17
-
17
+
18
18
  protected
19
19
  # determine the path that is used in the metric name for
20
20
  # the called controller action
@@ -4,312 +4,316 @@
4
4
  #
5
5
  # This class is not part of the public API. Avoid making calls on it directly.
6
6
  #
7
- module NewRelic::Agent::Instrumentation
8
- class MetricFrame
9
- attr_accessor :start # A Time instance for the start time, never nil
10
- attr_accessor :apdex_start # A Time instance used for calculating the apdex score, which
11
- # might end up being @start, or it might be further upstream if
12
- # we can find a request header for the queue entry time
13
- attr_accessor :exception,
14
- :filtered_params, :force_flag,
15
- :jruby_cpu_start, :process_cpu_start, :database_metric_name
16
-
17
- # Give the current metric frame a request context. Use this to
18
- # get the URI and referer. The request is interpreted loosely
19
- # as a Rack::Request or an ActionController::AbstractRequest.
20
- attr_accessor :request
21
-
22
-
23
- @@check_server_connection = false
24
- def self.check_server_connection=(value)
25
- @@check_server_connection = value
26
- end
27
- # Return the currently active metric frame, or nil. Call with +true+
28
- # to create a new metric frame if one is not already on the thread.
29
- def self.current(create_if_empty=nil)
30
- f = Thread.current[:newrelic_metric_frame]
31
- return f if f || !create_if_empty
32
-
33
- # Reconnect to the server if necessary. This is only done
34
- # for old versions of passenger that don't implement an explicit after_fork
35
- # event.
36
- NewRelic::Agent.instance.after_fork(:keep_retrying => false) if @@check_server_connection
37
-
38
- Thread.current[:newrelic_metric_frame] = new
39
- end
40
-
41
- # This is the name of the model currently assigned to database
42
- # measurements, overriding the default.
43
- def self.database_metric_name
44
- current && current.database_metric_name
45
- end
7
+ module NewRelic
8
+ module Agent
9
+ module Instrumentation
10
+ class MetricFrame
11
+ attr_accessor :start # A Time instance for the start time, never nil
12
+ attr_accessor :apdex_start # A Time instance used for calculating the apdex score, which
13
+ # might end up being @start, or it might be further upstream if
14
+ # we can find a request header for the queue entry time
15
+ attr_accessor :exception,
16
+ :filtered_params, :force_flag,
17
+ :jruby_cpu_start, :process_cpu_start, :database_metric_name
46
18
 
47
- def self.referer
48
- current && current.referer
49
- end
50
-
51
- @@java_classes_loaded = false
52
-
53
- if defined? JRuby
54
- begin
55
- require 'java'
56
- include_class 'java.lang.management.ManagementFactory'
57
- include_class 'com.sun.management.OperatingSystemMXBean'
58
- @@java_classes_loaded = true
59
- rescue Exception => e
60
- end
61
- end
62
-
63
- attr_reader :depth
64
-
65
- def initialize
66
- @start = Time.now
67
- @path_stack = [] # stack of [controller, path] elements
68
- @jruby_cpu_start = jruby_cpu_time
69
- @process_cpu_start = process_cpu
70
- end
71
-
72
- # Indicate that we are entering a measured controller action or task.
73
- # Make sure you unwind every push with a pop call.
74
- def push(m)
75
- NewRelic::Agent.instance.transaction_sampler.notice_first_scope_push(start)
76
- @path_stack.push NewRelic::MetricParser.for_metric_named(m)
77
- end
78
-
79
- # Indicate that you don't want to keep the currently saved transaction
80
- # information
81
- def self.abort_transaction!
82
- current.abort_transaction! if current
83
- end
84
-
85
- # For the current web transaction, return the path of the URI minus the host part and query string, or nil.
86
- def uri
87
- @uri ||= self.class.uri_from_request(@request) unless @request.nil?
88
- end
89
-
90
- # For the current web transaction, return the full referer, minus the host string, or nil.
91
- def referer
92
- @referer ||= self.class.referer_from_request(@request)
93
- end
94
-
95
- # Call this to ensure that the current transaction is not saved
96
- def abort_transaction!
97
- NewRelic::Agent.instance.transaction_sampler.ignore_transaction
98
- end
99
- # This needs to be called after entering the call to trace the controller action, otherwise
100
- # the controller action blames itself. It gets reset in the normal #pop call.
101
- def start_transaction
102
- NewRelic::Agent.instance.stats_engine.start_transaction metric_name
103
- # Only push the transaction context info once, on entry:
104
- if @path_stack.size == 1
105
- NewRelic::Agent.instance.transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
106
- end
107
- end
108
-
109
- def current_metric
110
- @path_stack.last
111
- end
112
-
113
- # Return the path, the part of the metric after the category
114
- def path
115
- @path_stack.last.last
116
- end
117
-
118
- # Unwind one stack level. It knows if it's back at the outermost caller and
119
- # does the appropriate wrapup of the context.
120
- def pop
121
- metric = @path_stack.pop
122
- if metric.nil?
123
- NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n ")}"
124
- end
125
- if @path_stack.empty?
126
- if NewRelic::Agent.is_execution_traced?
127
- cpu_burn = nil
128
- if @process_cpu_start
129
- cpu_burn = process_cpu - @process_cpu_start
130
- elsif @jruby_cpu_start
131
- cpu_burn = jruby_cpu_time - @jruby_cpu_start
132
- NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(cpu_burn)
19
+ # Give the current metric frame a request context. Use this to
20
+ # get the URI and referer. The request is interpreted loosely
21
+ # as a Rack::Request or an ActionController::AbstractRequest.
22
+ attr_accessor :request
23
+
24
+
25
+ @@check_server_connection = false
26
+ def self.check_server_connection=(value)
27
+ @@check_server_connection = value
28
+ end
29
+ # Return the currently active metric frame, or nil. Call with +true+
30
+ # to create a new metric frame if one is not already on the thread.
31
+ def self.current(create_if_empty=nil)
32
+ f = Thread.current[:newrelic_metric_frame]
33
+ return f if f || !create_if_empty
34
+
35
+ # Reconnect to the server if necessary. This is only done
36
+ # for old versions of passenger that don't implement an explicit after_fork
37
+ # event.
38
+ NewRelic::Agent.instance.after_fork(:keep_retrying => false) if @@check_server_connection
39
+
40
+ Thread.current[:newrelic_metric_frame] = new
41
+ end
42
+
43
+ # This is the name of the model currently assigned to database
44
+ # measurements, overriding the default.
45
+ def self.database_metric_name
46
+ current && current.database_metric_name
47
+ end
48
+
49
+ def self.referer
50
+ current && current.referer
51
+ end
52
+
53
+ @@java_classes_loaded = false
54
+
55
+ if defined? JRuby
56
+ begin
57
+ require 'java'
58
+ include_class 'java.lang.management.ManagementFactory'
59
+ include_class 'com.sun.management.OperatingSystemMXBean'
60
+ @@java_classes_loaded = true
61
+ rescue Exception => e
133
62
  end
134
- NewRelic::Agent.instance.transaction_sampler.notice_transaction_cpu_time(cpu_burn) if cpu_burn
135
- NewRelic::Agent.instance.histogram.process((Time.now - start).to_f) if metric.is_web_transaction?
136
- NewRelic::Agent.instance.transaction_sampler.notice_scope_empty
137
- end
138
- NewRelic::Agent.instance.stats_engine.end_transaction
139
- Thread.current[:newrelic_metric_frame] = nil
140
- else # path stack not empty
141
- # change the transaction name back to whatever was on the stack.
142
- NewRelic::Agent.instance.stats_engine.scope_name = metric_name
143
- end
144
- end
145
-
146
- # If we have an active metric frame, notice the error and increment the error metric.
147
- # Options:
148
- # * <tt>:request</tt> => Request object to get the uri and referer
149
- # * <tt>:uri</tt> => The request path, minus any request params or query string.
150
- # * <tt>:referer</tt> => The URI of the referer
151
- # * <tt>:metric</tt> => The metric name associated with the transaction
152
- # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
153
- # * <tt>:custom_params</tt> => Custom parameters
154
- # Anything left over is treated as custom params
155
-
156
- def self.notice_error(e, options={})
157
- if request = options.delete(:request)
158
- options[:referer] = referer_from_request(request)
159
- options[:uri] = uri_from_request(request)
160
- end
161
- if current
162
- current.notice_error(e, options)
163
- else
164
- NewRelic::Agent.instance.error_collector.notice_error(e, options)
165
- end
166
- end
167
-
168
- # Do not call this. Invoke the class method instead.
169
- def notice_error(e, options={}) # :nodoc:
170
- params = custom_parameters
171
- options[:referer] = referer if referer
172
- options[:request_params] = filtered_params if filtered_params
173
- options[:uri] = uri if uri
174
- options[:metric] = metric_name
175
- options.merge!(custom_parameters)
176
- if exception != e
177
- result = NewRelic::Agent.instance.error_collector.notice_error(e, options)
178
- self.exception = result if result
179
- end
180
- end
181
-
182
- # Add context parameters to the metric frame. This information will be passed in to errors
183
- # and transaction traces. Keys and Values should be strings, numbers or date/times.
184
- def self.add_custom_parameters(p)
185
- current.add_custom_parameters(p) if current
186
- end
187
-
188
- def self.custom_parameters
189
- (current && current.custom_parameters) ? current.custom_parameters : {}
190
- end
191
-
192
- def record_apdex()
193
- return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
194
- t = Time.now
195
- self.class.record_apdex(current_metric, t - start, t - apdex_start, !exception.nil?)
196
- end
197
-
198
- def metric_name
199
- return nil if @path_stack.empty?
200
- current_metric.name
201
- end
202
-
203
- # Return the array of metrics to record for the current metric frame.
204
- def recorded_metrics
205
- metrics = [ metric_name ]
206
- metrics += current_metric.summary_metrics if @path_stack.size == 1
207
- metrics
208
- end
209
-
210
- # Yield to a block that is run with a database metric name context. This means
211
- # the Database instrumentation will use this for the metric name if it does not
212
- # otherwise know about a model. This is re-entrant.
213
- #
214
- # * <tt>model</tt> is the DB model class
215
- # * <tt>method</tt> is the name of the finder method or other method to identify the operation with.
216
- #
217
- def with_database_metric_name(model, method)
218
- previous = @database_metric_name
219
- model_name = case model
220
- when Class
221
- model.name
222
- when String
223
- model
224
- else
225
- model.to_s
226
- end
227
- @database_metric_name = "ActiveRecord/#{model_name}/#{method}"
228
- yield
229
- ensure
230
- @database_metric_name=previous
231
- end
232
-
233
- def custom_parameters
234
- @custom_parameters ||= {}
235
- end
236
-
237
- def add_custom_parameters(p)
238
- custom_parameters.merge!(p)
239
- end
240
-
241
- def self.recording_web_transaction?
242
- if c = Thread.current[:newrelic_metric_frame]
243
- c.recording_web_transaction?
244
- end
245
- end
246
-
247
- def recording_web_transaction?
248
- current_metric && current_metric.is_web_transaction?
249
- end
250
-
251
- def is_web_transaction?(metric)
252
- 0 == metric.index("Controller")
253
- end
254
-
255
- # Make a safe attempt to get the referer from a request object, generally successful when
256
- # it's a Rack request.
257
- def self.referer_from_request(request)
258
- if request && request.respond_to?(:referer)
259
- request.referer.to_s.split('?').first
260
- end
261
- end
262
-
263
- # Make a safe attempt to get the URI, without the host and query string.
264
- def self.uri_from_request(request)
265
- approximate_uri = case
266
- when request.respond_to?(:fullpath) then request.fullpath
267
- when request.respond_to?(:path) then request.path
268
- when request.respond_to?(:request_uri) then request.request_uri
269
- when request.respond_to?(:uri) then request.uri
270
- when request.respond_to?(:url) then request.url
271
- end
272
- return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri
273
- end
274
-
275
- def self.record_apdex(current_metric, action_duration, total_duration, is_error)
276
- summary_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
277
- controller_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats(current_metric.apdex_metric_path, NewRelic::ApdexStats)
278
- update_apdex(summary_stat, total_duration, is_error)
279
- update_apdex(controller_stat, action_duration, is_error)
280
- end
63
+ end
64
+
65
+ attr_reader :depth
66
+
67
+ def initialize
68
+ @start = Time.now
69
+ @path_stack = [] # stack of [controller, path] elements
70
+ @jruby_cpu_start = jruby_cpu_time
71
+ @process_cpu_start = process_cpu
72
+ end
73
+
74
+ # Indicate that we are entering a measured controller action or task.
75
+ # Make sure you unwind every push with a pop call.
76
+ def push(m)
77
+ NewRelic::Agent.instance.transaction_sampler.notice_first_scope_push(start)
78
+ @path_stack.push NewRelic::MetricParser.for_metric_named(m)
79
+ end
80
+
81
+ # Indicate that you don't want to keep the currently saved transaction
82
+ # information
83
+ def self.abort_transaction!
84
+ current.abort_transaction! if current
85
+ end
86
+
87
+ # For the current web transaction, return the path of the URI minus the host part and query string, or nil.
88
+ def uri
89
+ @uri ||= self.class.uri_from_request(@request) unless @request.nil?
90
+ end
91
+
92
+ # For the current web transaction, return the full referer, minus the host string, or nil.
93
+ def referer
94
+ @referer ||= self.class.referer_from_request(@request)
95
+ end
96
+
97
+ # Call this to ensure that the current transaction is not saved
98
+ def abort_transaction!
99
+ NewRelic::Agent.instance.transaction_sampler.ignore_transaction
100
+ end
101
+ # This needs to be called after entering the call to trace the controller action, otherwise
102
+ # the controller action blames itself. It gets reset in the normal #pop call.
103
+ def start_transaction
104
+ NewRelic::Agent.instance.stats_engine.start_transaction metric_name
105
+ # Only push the transaction context info once, on entry:
106
+ if @path_stack.size == 1
107
+ NewRelic::Agent.instance.transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
108
+ end
109
+ end
110
+
111
+ def current_metric
112
+ @path_stack.last
113
+ end
114
+
115
+ # Return the path, the part of the metric after the category
116
+ def path
117
+ @path_stack.last.last
118
+ end
119
+
120
+ # Unwind one stack level. It knows if it's back at the outermost caller and
121
+ # does the appropriate wrapup of the context.
122
+ def pop
123
+ metric = @path_stack.pop
124
+ if metric.nil?
125
+ NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n ")}"
126
+ end
127
+ if @path_stack.empty?
128
+ if NewRelic::Agent.is_execution_traced?
129
+ cpu_burn = nil
130
+ if @process_cpu_start
131
+ cpu_burn = process_cpu - @process_cpu_start
132
+ elsif @jruby_cpu_start
133
+ cpu_burn = jruby_cpu_time - @jruby_cpu_start
134
+ NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(cpu_burn)
135
+ end
136
+ NewRelic::Agent.instance.transaction_sampler.notice_transaction_cpu_time(cpu_burn) if cpu_burn
137
+ NewRelic::Agent.instance.histogram.process((Time.now - start).to_f) if metric.is_web_transaction?
138
+ NewRelic::Agent.instance.transaction_sampler.notice_scope_empty
139
+ end
140
+ NewRelic::Agent.instance.stats_engine.end_transaction
141
+ Thread.current[:newrelic_metric_frame] = nil
142
+ else # path stack not empty
143
+ # change the transaction name back to whatever was on the stack.
144
+ NewRelic::Agent.instance.stats_engine.scope_name = metric_name
145
+ end
146
+ end
147
+
148
+ # If we have an active metric frame, notice the error and increment the error metric.
149
+ # Options:
150
+ # * <tt>:request</tt> => Request object to get the uri and referer
151
+ # * <tt>:uri</tt> => The request path, minus any request params or query string.
152
+ # * <tt>:referer</tt> => The URI of the referer
153
+ # * <tt>:metric</tt> => The metric name associated with the transaction
154
+ # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
155
+ # * <tt>:custom_params</tt> => Custom parameters
156
+ # Anything left over is treated as custom params
157
+
158
+ def self.notice_error(e, options={})
159
+ if request = options.delete(:request)
160
+ options[:referer] = referer_from_request(request)
161
+ options[:uri] = uri_from_request(request)
162
+ end
163
+ if current
164
+ current.notice_error(e, options)
165
+ else
166
+ NewRelic::Agent.instance.error_collector.notice_error(e, options)
167
+ end
168
+ end
169
+
170
+ # Do not call this. Invoke the class method instead.
171
+ def notice_error(e, options={}) # :nodoc:
172
+ params = custom_parameters
173
+ options[:referer] = referer if referer
174
+ options[:request_params] = filtered_params if filtered_params
175
+ options[:uri] = uri if uri
176
+ options[:metric] = metric_name
177
+ options.merge!(custom_parameters)
178
+ if exception != e
179
+ result = NewRelic::Agent.instance.error_collector.notice_error(e, options)
180
+ self.exception = result if result
181
+ end
182
+ end
183
+
184
+ # Add context parameters to the metric frame. This information will be passed in to errors
185
+ # and transaction traces. Keys and Values should be strings, numbers or date/times.
186
+ def self.add_custom_parameters(p)
187
+ current.add_custom_parameters(p) if current
188
+ end
189
+
190
+ def self.custom_parameters
191
+ (current && current.custom_parameters) ? current.custom_parameters : {}
192
+ end
193
+
194
+ def record_apdex()
195
+ return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
196
+ t = Time.now
197
+ self.class.record_apdex(current_metric, t - start, t - apdex_start, !exception.nil?)
198
+ end
199
+
200
+ def metric_name
201
+ return nil if @path_stack.empty?
202
+ current_metric.name
203
+ end
204
+
205
+ # Return the array of metrics to record for the current metric frame.
206
+ def recorded_metrics
207
+ metrics = [ metric_name ]
208
+ metrics += current_metric.summary_metrics if @path_stack.size == 1
209
+ metrics
210
+ end
211
+
212
+ # Yield to a block that is run with a database metric name context. This means
213
+ # the Database instrumentation will use this for the metric name if it does not
214
+ # otherwise know about a model. This is re-entrant.
215
+ #
216
+ # * <tt>model</tt> is the DB model class
217
+ # * <tt>method</tt> is the name of the finder method or other method to identify the operation with.
218
+ #
219
+ def with_database_metric_name(model, method)
220
+ previous = @database_metric_name
221
+ model_name = case model
222
+ when Class
223
+ model.name
224
+ when String
225
+ model
226
+ else
227
+ model.to_s
228
+ end
229
+ @database_metric_name = "ActiveRecord/#{model_name}/#{method}"
230
+ yield
231
+ ensure
232
+ @database_metric_name=previous
233
+ end
234
+
235
+ def custom_parameters
236
+ @custom_parameters ||= {}
237
+ end
238
+
239
+ def add_custom_parameters(p)
240
+ custom_parameters.merge!(p)
241
+ end
242
+
243
+ def self.recording_web_transaction?
244
+ if c = Thread.current[:newrelic_metric_frame]
245
+ c.recording_web_transaction?
246
+ end
247
+ end
248
+
249
+ def recording_web_transaction?
250
+ current_metric && current_metric.is_web_transaction?
251
+ end
252
+
253
+ def is_web_transaction?(metric)
254
+ 0 == metric.index("Controller")
255
+ end
256
+
257
+ # Make a safe attempt to get the referer from a request object, generally successful when
258
+ # it's a Rack request.
259
+ def self.referer_from_request(request)
260
+ if request && request.respond_to?(:referer)
261
+ request.referer.to_s.split('?').first
262
+ end
263
+ end
264
+
265
+ # Make a safe attempt to get the URI, without the host and query string.
266
+ def self.uri_from_request(request)
267
+ approximate_uri = case
268
+ when request.respond_to?(:fullpath) then request.fullpath
269
+ when request.respond_to?(:path) then request.path
270
+ when request.respond_to?(:request_uri) then request.request_uri
271
+ when request.respond_to?(:uri) then request.uri
272
+ when request.respond_to?(:url) then request.url
273
+ end
274
+ return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri # '
275
+ end
276
+
277
+ def self.record_apdex(current_metric, action_duration, total_duration, is_error)
278
+ summary_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
279
+ controller_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats(current_metric.apdex_metric_path, NewRelic::ApdexStats)
280
+ update_apdex(summary_stat, total_duration, is_error)
281
+ update_apdex(controller_stat, action_duration, is_error)
282
+ end
283
+
284
+ # Record an apdex value for the given stat. when `failed`
285
+ # the apdex should be recorded as a failure regardless of duration.
286
+ def self.update_apdex(stat, duration, failed)
287
+ duration = duration.to_f
288
+ apdex_t = NewRelic::Control.instance.apdex_t
289
+ case
290
+ when failed
291
+ stat.record_apdex_f
292
+ when duration <= apdex_t
293
+ stat.record_apdex_s
294
+ when duration <= 4 * apdex_t
295
+ stat.record_apdex_t
296
+ else
297
+ stat.record_apdex_f
298
+ end
299
+ end
300
+
301
+ private
302
+
303
+ def process_cpu
304
+ return nil if defined? JRuby
305
+ p = Process.times
306
+ p.stime + p.utime
307
+ end
308
+
309
+ def jruby_cpu_time # :nodoc:
310
+ return nil unless @@java_classes_loaded
311
+ threadMBean = ManagementFactory.getThreadMXBean()
312
+ java_utime = threadMBean.getCurrentThreadUserTime() # ns
313
+ -1 == java_utime ? 0.0 : java_utime/1e9
314
+ end
281
315
 
282
- # Record an apdex value for the given stat. non-nil 'failed'
283
- # the apdex should be recorded as a failure regardless of duration.
284
- def self.update_apdex(stat, duration, failed)
285
- duration = duration.to_f
286
- apdex_t = NewRelic::Control.instance.apdex_t
287
- case
288
- when failed
289
- stat.record_apdex_f
290
- when duration <= apdex_t
291
- stat.record_apdex_s
292
- when duration <= 4 * apdex_t
293
- stat.record_apdex_t
294
- else
295
- stat.record_apdex_f
296
316
  end
297
- end
298
-
299
- private
300
-
301
- def process_cpu
302
- return nil if defined? JRuby
303
- p = Process.times
304
- p.stime + p.utime
305
- end
306
-
307
- def jruby_cpu_time # :nodoc:
308
- return nil unless @@java_classes_loaded
309
- threadMBean = ManagementFactory.getThreadMXBean()
310
- java_utime = threadMBean.getCurrentThreadUserTime() # ns
311
- -1 == java_utime ? 0.0 : java_utime/1e9
312
317
  end
313
-
314
318
  end
315
- end
319
+ end