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

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

Constant Summary collapse

@@check_server_connection =
false
@@java_classes_loaded =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeMetricFrame

Returns a new instance of MetricFrame.



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

def initialize
  @start = Time.now.to_f
  @path_stack = [] # stack of [controller, path] elements
  @jruby_cpu_start = jruby_cpu_time
  @process_cpu_start = process_cpu
end

Instance Attribute Details

#apdex_startObject

Returns the value of attribute apdex_start.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def apdex_start
  @apdex_start
end

#database_metric_nameObject

Returns the value of attribute database_metric_name.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def database_metric_name
  @database_metric_name
end

#depthObject (readonly)

Returns the value of attribute depth.



59
60
61
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 59

def depth
  @depth
end

#exceptionObject

Returns the value of attribute exception.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def exception
  @exception
end

#filtered_paramsObject

Returns the value of attribute filtered_params.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def filtered_params
  @filtered_params
end

#force_flagObject

Returns the value of attribute force_flag.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def force_flag
  @force_flag
end

#jruby_cpu_startObject

Returns the value of attribute jruby_cpu_start.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def jruby_cpu_start
  @jruby_cpu_start
end

#process_cpu_startObject

Returns the value of attribute process_cpu_start.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

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.



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

def request
  @request
end

#startObject

Returns the value of attribute start.



9
10
11
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 9

def start
  @start
end

Class Method Details

.abort_transaction!Object

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



77
78
79
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 77

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.



179
180
181
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 179

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

.check_server_connection=(value) ⇒ Object



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

def self.check_server_connection=(value)
  @@check_server_connection = value
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.



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 25

def self.current(create_if_empty=nil)
  f = Thread.current[:newrelic_metric_frame]
  return f if f || !create_if_empty
  
  # Reconnect to the server if necessary.  This is only done
  # for old versions of passenger that don't implement an explicit after_fork
  # event.
  NewRelic::Agent.instance.after_fork(:keep_retrying => false) if @@check_server_connection
  
  Thread.current[:newrelic_metric_frame] = new
end

.custom_parametersObject



183
184
185
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 183

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.



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

def self.database_metric_name
  current && current.database_metric_name
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



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 151

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

.recording_web_transaction?Boolean

Returns:

  • (Boolean)


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

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

.refererObject



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

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.



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

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

.update_apdex(stat, duration, failed) ⇒ Object

Record an apdex value for the given stat. non-nil ‘failed’ the apdex should be recorded as a failure regardless of duration.



277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 277

def self.update_apdex(stat, duration, failed)
  apdex_t = NewRelic::Control.instance.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.



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

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

Instance Method Details

#abort_transaction!Object

Call this to ensure that the current transaction is not saved



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

def abort_transaction!
  NewRelic::Agent.instance.transaction_sampler.ignore_transaction
end

#add_custom_parameters(p) ⇒ Object



241
242
243
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 241

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

#categoryObject



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

def category
  @path_stack.last.first  
end

#custom_parametersObject



237
238
239
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 237

def custom_parameters
  @custom_parameters ||= {}
end

#metric_nameObject



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

def metric_name
  return nil if @path_stack.empty?
  category + '/' + path 
end

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

Do not call this. Invoke the class method instead.



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 164

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
    NewRelic::Agent.instance.error_collector.notice_error(e, options)
    self.exception = e
  end
end

#pathObject



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

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.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 115

def pop
  category, path = @path_stack.pop
  if category.nil?
    NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n   ")}"
  end
  if @path_stack.empty?
    if NewRelic::Agent.is_execution_traced?
      cpu_burn = nil
      if @process_cpu_start
        cpu_burn = process_cpu - @process_cpu_start
      elsif @jruby_cpu_start
        cpu_burn = jruby_cpu_time - @jruby_cpu_start
        NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(cpu_burn)
      end
      NewRelic::Agent.instance.transaction_sampler.notice_transaction_cpu_time(cpu_burn) if cpu_burn
      NewRelic::Agent.instance.histogram.process(Time.now.to_f - start) if recording_web_transaction?(category)
      NewRelic::Agent.instance.transaction_sampler.notice_scope_empty      
    end      
    NewRelic::Agent.instance.stats_engine.end_transaction
    Thread.current[:newrelic_metric_frame] = nil
  else # path stack not empty
    # change the transaction name back to whatever was on the stack.  
    NewRelic::Agent.instance.stats_engine.scope_name = metric_name
  end
end

#push(category, path) ⇒ Object

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



70
71
72
73
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 70

def push(category, path)
  NewRelic::Agent.instance.transaction_sampler.notice_first_scope_push(start)
  @path_stack.push [category, path]
end

#record_apdexObject



187
188
189
190
191
192
193
194
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 187

def record_apdex
  return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
  ending = Time.now.to_f
  summary_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
  controller_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex/#{path}", NewRelic::ApdexStats)
  self.class.update_apdex(summary_stat, ending - apdex_start, exception)
  self.class.update_apdex(controller_stat, ending - start, exception)
end

#recorded_metricsObject

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



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 202

def recorded_metrics
  metrics = [ metric_name ]
  if @path_stack.size == 1
    if recording_web_transaction?
      metrics += ["HttpDispatcher"]
    else
      metrics += ["#{category}/all", "OtherTransaction/all"]
    end
  end
  metrics
end

#recording_web_transaction?(cat = category) ⇒ Boolean

Returns:

  • (Boolean)


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

def recording_web_transaction?(cat = category)
  0 == cat.index("Controller")
end

#refererObject

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



87
88
89
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 87

def referer
  @referer ||= self.class.referer_from_request(@request)
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.



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

def start_transaction
  NewRelic::Agent.instance.stats_engine.start_transaction metric_name
  # Only push the transaction context info once, on entry:
  if @path_stack.size == 1
    NewRelic::Agent.instance.transaction_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.



82
83
84
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 82

def uri
  @uri ||= self.class.uri_from_request(@request) unless @request.nil?
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.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/new_relic/agent/instrumentation/metric_frame.rb', line 221

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