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

Extended by:
TraceExecutionScoped
Included in:
NewRelic::Agent::MethodTracer::InstanceMethods, TraceExecutionScoped
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



104
105
106
# File 'lib/new_relic/agent/method_tracer.rb', line 104

def agent_instance
  NewRelic::Agent.instance
end

#get_stats_scoped(first_name, scoped_metric_only) ⇒ Object

returns a scoped metric stat for the specified name



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

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



129
130
131
# File 'lib/new_relic/agent/method_tracer.rb', line 129

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

#has_parent?Boolean

Returns:

  • (Boolean)


208
209
210
# File 'lib/new_relic/agent/method_tracer.rb', line 208

def has_parent?
  !NewRelic::Agent::Transaction.parent.nil?
end

#in_transaction?Boolean

Returns:

  • (Boolean)


204
205
206
# File 'lib/new_relic/agent/method_tracer.rb', line 204

def in_transaction?
  NewRelic::Agent::Transaction.in_transaction?
end

#log_errors(code_area) ⇒ Object

helper for logging errors to the newrelic_agent.log properly. Logs the error at error level



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

def log_errors(code_area)
  yield
rescue => e
  ::NewRelic::Agent.logger.error("Caught exception in #{code_area}.", e)
end

#metrics_for_current_transaction(first_name, other_names, options) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/new_relic/agent/method_tracer.rb', line 185

def metrics_for_current_transaction(first_name, other_names, options)
  metrics = []

  if !options[:scoped_metric_only]
    metrics += other_names.map { |n| NewRelic::MetricSpec.new(n) }
  end

  if options[:metric]
    if !options[:scoped_metric_only]
      metrics << NewRelic::MetricSpec.new(first_name)
    end
    if in_transaction? && !options[:transaction]
      metrics << NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)
    end
  end

  metrics
end

#metrics_for_parent_transaction(first_name, options) ⇒ Object



212
213
214
215
216
217
218
# File 'lib/new_relic/agent/method_tracer.rb', line 212

def metrics_for_parent_transaction(first_name, options)
  if has_parent? && options[:metric] && options[:transaction]
    [NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)]
  else
    []
  end
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



156
157
158
# File 'lib/new_relic/agent/method_tracer.rb', line 156

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.



145
146
147
# File 'lib/new_relic/agent/method_tracer.rb', line 145

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

#record_metrics(first_name, other_names, duration, exclusive, options) ⇒ Object



220
221
222
223
224
225
226
227
228
229
# File 'lib/new_relic/agent/method_tracer.rb', line 220

def record_metrics(first_name, other_names, duration, exclusive, options)
  metrics = metrics_for_current_transaction(first_name, other_names, options)
  stat_engine.record_metrics_internal(metrics, duration, exclusive)

  parent_metrics = metrics_for_parent_transaction(first_name, options)
  parent_metrics.each do |metric|
    parent_txn = NewRelic::Agent::Transaction.parent
    parent_txn.stats_hash.record(metric, duration, exclusive)
  end
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



136
137
138
# File 'lib/new_relic/agent/method_tracer.rb', line 136

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

#stat_engineObject

Shorthand to return the current statistics engine



120
121
122
# File 'lib/new_relic/agent/method_tracer.rb', line 120

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)


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

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.



260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/new_relic/agent/method_tracer.rb', line 260

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)
  metric_names = Array(metric_names)
  first_name = metric_names.shift
  start_time, expected_scope = trace_execution_scoped_header(options)
  begin
    yield
  ensure
    trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, options)
  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.



239
240
241
242
243
244
245
246
247
248
249
# File 'lib/new_relic/agent/method_tracer.rb', line 239

def trace_execution_scoped_footer(t0, first_name, metric_names, expected_scope, options, t1=Time.now.to_f)
  log_errors("trace_method_execution footer") do
    pop_flag!(options[:force])
    if expected_scope
      scope = stat_engine.pop_scope(expected_scope, first_name, t1)
      duration = t1 - t0
      exclusive = duration - scope.children_time
      record_metrics(first_name, metric_names, duration, exclusive, options)
    end
  end
end

#trace_execution_scoped_header(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.



175
176
177
178
179
180
181
182
183
# File 'lib/new_relic/agent/method_tracer.rb', line 175

def trace_execution_scoped_header(options, t0=Time.now.to_f)
  scope = log_errors("trace_execution_scoped header") do
    push_flag!(options[:force])
    scope = stat_engine.push_scope(:method_tracer, 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)


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

def traced?
  NewRelic::Agent.is_execution_traced?
end