Class: NewRelic::Agent::Instrumentation::MetricFrame

Inherits:
Object
  • Object
show all
Includes:
Pop
Defined in:
lib/new_relic/agent/instrumentation/metric_frame.rb,
lib/new_relic/agent/instrumentation/metric_frame/pop.rb

Defined Under Namespace

Modules: Pop

Constant Summary collapse

@@java_classes_loaded =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Pop

#clear_thread_metric_frame!, #cpu_burn, #current_stack_metric, #end_transaction!, #handle_empty_path_stack, #jruby_cpu_burn, #log_underflow, #normal_cpu_burn, #notice_scope_empty, #notify_transaction_sampler, #record_jruby_cpu_burn, #record_transaction_cpu, #set_new_scope!, #traced?

Constructor Details

#initializeMetricFrame

Returns a new instance of MetricFrame.



66
67
68
69
70
71
72
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 66

def initialize
  @start = Time.now
  @path_stack = [] # stack of [controller, path] elements
  @jruby_cpu_start = jruby_cpu_time
  @process_cpu_start = process_cpu
  Thread.current[:last_metric_frame] = self
end

Instance Attribute Details

#apdex_startObject

A Time instance used for calculating the apdex score, which



17
18
19
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 17

def apdex_start
  @apdex_start
end

#database_metric_nameObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def database_metric_name
  @database_metric_name
end

#depthObject (readonly)

Returns the value of attribute depth.



64
65
66
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 64

def depth
  @depth
end

#exceptionObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def exception
  @exception
end

#filtered_paramsObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def filtered_params
  @filtered_params
end

#force_flagObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def force_flag
  @force_flag
end

#jruby_cpu_startObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def jruby_cpu_start
  @jruby_cpu_start
end

#process_cpu_startObject

might end up being @start, or it might be further upstream if we can find a request header for the queue entry time



20
21
22
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 20

def process_cpu_start
  @process_cpu_start
end

#requestObject

Give the current metric frame a request context. Use this to get the URI and referer. The request is interpreted loosely as a Rack::Request or an ActionController::AbstractRequest.



27
28
29
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 27

def request
  @request
end

#startObject

A Time instance for the start time, never nil



16
17
18
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 16

def start
  @start
end

Class Method Details

.abort_transaction!Object

Indicate that you don’t want to keep the currently saved transaction information



100
101
102
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 100

def self.abort_transaction!
  current.abort_transaction! if current
end

.add_custom_parameters(p) ⇒ Object

Add context parameters to the metric frame. This information will be passed in to errors and transaction traces. Keys and Values should be strings, numbers or date/times.



190
191
192
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 190

def self.add_custom_parameters(p)
  current.add_custom_parameters(p) if current
end

.agentObject



48
49
50
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 48

def self.agent
  NewRelic::Agent.instance
end

.current(create_if_empty = nil) ⇒ Object

Return the currently active metric frame, or nil. Call with true to create a new metric frame if one is not already on the thread.



32
33
34
35
36
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 32

def self.current(create_if_empty=nil)
  f = Thread.current[:newrelic_metric_frame]
  return f if f || !create_if_empty
  Thread.current[:newrelic_metric_frame] = new
end

.custom_parametersObject



194
195
196
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 194

def self.custom_parameters
  (current && current.custom_parameters) ? current.custom_parameters : {}
end

.database_metric_nameObject

This is the name of the model currently assigned to database measurements, overriding the default.



40
41
42
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 40

def self.database_metric_name
  current && current.database_metric_name
end

.initialize_apdex(metric_name) ⇒ Object

Apdex min and max values should be initialized to the current apdex_t



314
315
316
317
318
319
320
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 314

def self.initialize_apdex(metric_name)
  stats = agent.stats_engine.get_stats_no_scope(metric_name)
  apdex_t = TransactionInfo.get.apdex_t
  stats.min_call_time = apdex_t
  stats.max_call_time = apdex_t
  return stats
end

.notice_error(e, options = {}) ⇒ Object

If we have an active metric frame, notice the error and increment the error metric. Options:

  • :request => Request object to get the uri and referer

  • :uri => The request path, minus any request params or query string.

  • :referer => The URI of the referer

  • :metric => The metric name associated with the transaction

  • :request_params => Request parameters, already filtered if necessary

  • :custom_params => Custom parameters

Anything left over is treated as custom params



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 161

def self.notice_error(e, options={})
  request = options.delete(:request)
  if request
    options[:referer] = referer_from_request(request)
    options[:uri] = uri_from_request(request)
  end
  if current
    current.notice_error(e, options)
  else
    agent.error_collector.notice_error(e, options)
  end
end

.record_apdex(current_metric, action_duration, total_duration, is_error) ⇒ Object



302
303
304
305
306
307
308
309
310
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 302

def self.record_apdex(current_metric, action_duration, total_duration, is_error)
  summary_stat = agent.stats_engine.lookup_stats("Apdex") ||
    initialize_apdex("Apdex")
  update_apdex(summary_stat, total_duration, is_error)

  controller_stat = agent.stats_engine.lookup_stats(current_metric.apdex_metric_path) ||
    initialize_apdex(current_metric.apdex_metric_path)
  update_apdex(controller_stat, action_duration, is_error)
end

.recording_web_transaction?Boolean

Returns:

  • (Boolean)


267
268
269
270
271
272
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 267

def self.recording_web_transaction?
  c = Thread.current[:newrelic_metric_frame]
  if c
    c.recording_web_transaction?
  end
end

.refererObject



44
45
46
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 44

def self.referer
  current && current.referer
end

.referer_from_request(request) ⇒ Object

Make a safe attempt to get the referer from a request object, generally successful when it’s a Rack request.



284
285
286
287
288
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 284

def self.referer_from_request(request)
  if request && request.respond_to?(:referer)
    request.referer.to_s.split('?').first
  end
end

.set_user_attributes(attributes) ⇒ Object



198
199
200
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 198

def self.set_user_attributes(attributes)
  current.set_user_attributes(attributes) if current 
end

.update_apdex(stat, duration, failed) ⇒ Object

Record an apdex value for the given stat. when ‘failed` the apdex should be recorded as a failure regardless of duration.



324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 324

def self.update_apdex(stat, duration, failed)
  duration = duration.to_f
  apdex_t = TransactionInfo.get.apdex_t
  case
  when failed
    stat.record_apdex_f
  when duration <= apdex_t
    stat.record_apdex_s
  when duration <= 4 * apdex_t
    stat.record_apdex_t
  else
    stat.record_apdex_f
  end
end

.uri_from_request(request) ⇒ Object

Make a safe attempt to get the URI, without the host and query string.



291
292
293
294
295
296
297
298
299
300
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 291

def self.uri_from_request(request)
  approximate_uri = case
                    when request.respond_to?(:fullpath) then request.fullpath
                    when request.respond_to?(:path) then request.path
                    when request.respond_to?(:request_uri) then request.request_uri
                    when request.respond_to?(:uri) then request.uri
                    when request.respond_to?(:url) then request.url
                    end
  return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri # '
end

.user_attributesObject



202
203
204
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 202

def self.user_attributes
  (current) ? current.user_attributes : {}
end

Instance Method Details

#abort_transaction!Object

Call this to ensure that the current transaction is not saved



115
116
117
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 115

def abort_transaction!
  transaction_sampler.ignore_transaction
end

#add_custom_parameters(p) ⇒ Object



259
260
261
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 259

def add_custom_parameters(p)
  custom_parameters.merge!(p)
end

#current_metricObject



130
131
132
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 130

def current_metric
  @path_stack.last
end

#custom_parametersObject



247
248
249
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 247

def custom_parameters
  @custom_parameters ||= {}
end

#is_web_transaction?(metric) ⇒ Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 278

def is_web_transaction?(metric)
  0 == metric.index("Controller")
end

#metric_nameObject



212
213
214
215
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 212

def metric_name
  return nil if @path_stack.empty?
  current_metric.name
end

#notice_error(e, options = {}) ⇒ Object

Do not call this. Invoke the class method instead.



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 175

def notice_error(e, options={}) # :nodoc:
  params = custom_parameters
  options[:referer] = referer if referer
  options[:request_params] = filtered_params if filtered_params
  options[:uri] = uri if uri
  options[:metric] = metric_name
  options.merge!(custom_parameters)
  if exception != e
    result = agent.error_collector.notice_error(e, options)
    self.exception = result if result
  end
end

#pathObject

Return the path, the part of the metric after the category



135
136
137
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 135

def path
  @path_stack.last.last
end

#popObject

Unwind one stack level. It knows if it’s back at the outermost caller and does the appropriate wrapup of the context.



141
142
143
144
145
146
147
148
149
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 141

def pop
  metric = @path_stack.pop
  log_underflow if metric.nil?
  if @path_stack.empty?
    handle_empty_path_stack(metric)
  else
    set_new_scope!(current_stack_metric)
  end
end

#push(m) ⇒ Object

Indicate that we are entering a measured controller action or task. Make sure you unwind every push with a pop call.



92
93
94
95
96
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 92

def push(m)
  transaction_sampler.notice_first_scope_push(start)
  sql_sampler.notice_first_scope_push(start)
  @path_stack.push NewRelic::MetricParser::MetricParser.for_metric_named(m)
end

#queue_timeObject



255
256
257
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 255

def queue_time
  start - apdex_start
end

#record_apdexObject



206
207
208
209
210
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 206

def record_apdex()
  return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
  t = Time.now
  self.class.record_apdex(current_metric, t - start, t - apdex_start, !exception.nil?)
end

#recorded_metricsObject

Return the array of metrics to record for the current metric frame.



218
219
220
221
222
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 218

def recorded_metrics
  metrics = [ metric_name ]
  metrics += current_metric.summary_metrics if @path_stack.size == 1
  metrics
end

#recording_web_transaction?Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 274

def recording_web_transaction?
  current_metric && current_metric.is_web_transaction?
end

#refererObject

For the current web transaction, return the full referer, minus the host string, or nil.



110
111
112
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 110

def referer
  @referer ||= self.class.referer_from_request(@request)
end

#set_user_attributes(attributes) ⇒ Object



263
264
265
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 263

def set_user_attributes(attributes)
  user_attributes.merge!(attributes)
end

#start_transactionObject

This needs to be called after entering the call to trace the controller action, otherwise the controller action blames itself. It gets reset in the normal #pop call.



121
122
123
124
125
126
127
128
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 121

def start_transaction
  agent.stats_engine.start_transaction metric_name
  # Only push the transaction context info once, on entry:
  if @path_stack.size == 1
    transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
    sql_sampler.notice_transaction(metric_name, uri, filtered_params)
  end
end

#uriObject

For the current web transaction, return the path of the URI minus the host part and query string, or nil.



105
106
107
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 105

def uri
  @uri ||= self.class.uri_from_request(@request) unless @request.nil?
end

#user_attributesObject



251
252
253
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 251

def user_attributes
  @user_atrributes ||= {}
end

#with_database_metric_name(model, method) ⇒ Object

Yield to a block that is run with a database metric name context. This means the Database instrumentation will use this for the metric name if it does not otherwise know about a model. This is re-entrant.

  • model is the DB model class

  • method is the name of the finder method or other method to identify the operation with.



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 231

def with_database_metric_name(model, method)
  previous = @database_metric_name
  model_name = case model
               when Class
                 model.name
               when String
                 model
               else
                 model.to_s
               end
  @database_metric_name = "ActiveRecord/#{model_name}/#{method}"
  yield
ensure
  @database_metric_name=previous
end