Module: NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped

Included in:
NewRelic::Agent::MethodTracer::InstanceMethods
Defined in:
lib/new_relic/agent/method_tracer.rb

Overview

Refactored out of the previous trace_execution_scoped method, most methods in this module relate to code used in the #trace_execution_scoped method in this module

Instance Method Summary collapse

Instance Method Details

#agent_instanceObject

Shorthand to return the NewRelic::Agent.instance



101
102
103
# File 'lib/new_relic/agent/method_tracer.rb', line 101

def agent_instance
  NewRelic::Agent.instance
end

#get_metric_stats(metrics, options) ⇒ Object

returns an array containing the first metric, and an array of other unscoped statistics we should also record along side it



140
141
142
143
144
145
146
147
148
# File 'lib/new_relic/agent/method_tracer.rb', line 140

def get_metric_stats(metrics, options)
  metrics = Array(metrics)
  first_name = metrics.shift
  stats = metrics.map do | name |
    get_stats_unscoped(name)
  end
  stats.unshift(main_stat(first_name, options)) if options[:metric]
  [first_name, stats]
end

#get_stats_scoped(first_name, scoped_metric_only) ⇒ Object

returns a scoped metric stat for the specified name



122
123
124
# File 'lib/new_relic/agent/method_tracer.rb', line 122

def get_stats_scoped(first_name, scoped_metric_only)
  stat_engine.get_stats(first_name, true, scoped_metric_only)
end

#get_stats_unscoped(name) ⇒ Object

Shorthand method to get stats from the stat engine



126
127
128
# File 'lib/new_relic/agent/method_tracer.rb', line 126

def get_stats_unscoped(name)
  stat_engine.get_stats_no_scope(name)
end

#log_errors(code_area, metric) ⇒ Object

helper for logging errors to the newrelic_agent.log properly. Logs the error at error level, and includes a backtrace if we’re running at debug level



180
181
182
183
184
185
# File 'lib/new_relic/agent/method_tracer.rb', line 180

def log_errors(code_area, metric)
  yield
rescue => e
  NewRelic::Control.instance.log.error("Caught exception in #{code_area}. Metric name = #{metric}, exception = #{e}")
  NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
end

#main_stat(metric, options) ⇒ Object

the main statistic we should record in #trace_execution_scoped - a scoped metric provided by the first item in the metric array



133
134
135
# File 'lib/new_relic/agent/method_tracer.rb', line 133

def main_stat(metric, options)
  get_stats_scoped(metric, options[:scoped_metric_only])
end

#pop_flag!(forced) ⇒ Object

delegates to #agent_instance to pop the trace execution flag, only if execution of this metric is forced. otherwise this is taken care of for us automatically.

This ends the forced recording of metrics within the #trace_execution_scoped block



173
174
175
# File 'lib/new_relic/agent/method_tracer.rb', line 173

def pop_flag!(forced)
  agent_instance.pop_trace_execution_flag if forced
end

#push_flag!(forced) ⇒ Object

delegates to #agent_instance to push a trace execution flag, only if execution of this metric is forced.

This causes everything scoped inside this metric to be recorded, even if the parent transaction is generally not.



162
163
164
# File 'lib/new_relic/agent/method_tracer.rb', line 162

def push_flag!(forced)
  agent_instance.push_trace_execution_flag(true) if forced
end

#set_if_nil(hash, key) ⇒ Object

Helper for setting a hash key if the hash key is nil, instead of the default ||= behavior which sets if it is false as well



153
154
155
# File 'lib/new_relic/agent/method_tracer.rb', line 153

def set_if_nil(hash, key)
  hash[key] = true if hash[key].nil?
end

#stat_engineObject

Shorthand to return the current statistics engine



117
118
119
# File 'lib/new_relic/agent/method_tracer.rb', line 117

def stat_engine
  agent_instance.stats_engine
end

#trace_disabled?(options) ⇒ Boolean

Tracing is disabled if we are not in a traced context and no force option is supplied

Returns:

  • (Boolean)


112
113
114
# File 'lib/new_relic/agent/method_tracer.rb', line 112

def trace_disabled?(options)
  !(traced? || options[:force])
end

#trace_execution_scoped(metric_names, options = {}) ⇒ Object

Trace a given block with stats and keep track of the caller. See NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer for a description of the arguments. metric_names is either a single name or an array of metric names. If more than one metric is passed, the produce_metric option only applies to the first. The others are always recorded. Only the first metric is pushed onto the scope stack.

Generally you pass an array of metric names if you want to record the metric under additional categories, but generally this *should never ever be done*. Most of the time you can aggregate on the server.



235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/new_relic/agent/method_tracer.rb', line 235

def trace_execution_scoped(metric_names, options={})
  return yield if trace_disabled?(options)
  set_if_nil(options, :metric)
  set_if_nil(options, :deduct_call_time_from_parent)
  first_name, metric_stats = get_metric_stats(metric_names, options)
  start_time, expected_scope = trace_execution_scoped_header(first_name, options)
  begin
    yield
  ensure
    trace_execution_scoped_footer(start_time, first_name, metric_stats, expected_scope, options[:force])
  end
end

Handles the end of the #trace_execution_scoped method - calculating the time taken, popping the tracing flag if needed, deducting time taken by children, and tracing the subsidiary unscoped metrics if any

this method fails safely if the header does not manage to push the scope onto the stack - it simply does not trace any metrics.



212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/new_relic/agent/method_tracer.rb', line 212

def trace_execution_scoped_footer(t0, first_name, metric_stats, expected_scope, forced, t1=Time.now.to_f)
  log_errors("trace_method_execution footer", first_name) do
    duration = t1 - t0

    pop_flag!(forced)
    if expected_scope
      scope = stat_engine.pop_scope(expected_scope, duration, t1)
      exclusive = duration - scope.children_time
      metric_stats.each { |stats| stats.trace_call(duration, exclusive) }
    end
  end
end

#trace_execution_scoped_header(metric, options, t0 = Time.now.to_f) ⇒ Object

provides the header for our traced execution scoped method - gets the initial time, sets the tracing flag if needed, and pushes the scope onto the metric stack logs any errors that occur and returns the start time and the scope so that we can check for it later, to maintain sanity. If the scope stack becomes unbalanced, this transaction loses meaning.



194
195
196
197
198
199
200
201
202
# File 'lib/new_relic/agent/method_tracer.rb', line 194

def trace_execution_scoped_header(metric, options, t0=Time.now.to_f)
  scope = log_errors("trace_execution_scoped header", metric) do
    push_flag!(options[:force])
    scope = stat_engine.push_scope(metric, t0, options[:deduct_call_time_from_parent])
  end
  # needed in case we have an error, above, to always return
  # the start time.
  [t0, scope]
end

#traced?Boolean

Shorthand to return the status of tracing

Returns:

  • (Boolean)


106
107
108
# File 'lib/new_relic/agent/method_tracer.rb', line 106

def traced?
  NewRelic::Agent.is_execution_traced?
end