Class: NewRelic::Agent::Transaction

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

Defined Under Namespace

Modules: Pop

Constant Summary collapse

OVERVIEW_SPECS =
[
  [:webDuration,      MetricSpec.new('HttpDispatcher')],
  [:queueDuration,    MetricSpec.new('WebFrontend/QueueTime')],
  [:externalDuration, MetricSpec.new('External/allWeb')],
  [:databaseDuration, MetricSpec.new('ActiveRecord/all')],
  [:gcCumulative,     MetricSpec.new("GC/cumulative")],
  [:memcacheDuration, MetricSpec.new('Memcache/allWeb')]
]
APDEX_METRIC_SPEC =
NewRelic::MetricSpec.new('Apdex').freeze
@@java_classes_loaded =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Pop

#cpu_burn, #current_stack_metric, #jruby_cpu_burn, #log_underflow, #normal_cpu_burn, #record_jruby_cpu_burn, #record_transaction_cpu, #traced?

Constructor Details

#initialize(type = :controller, options = {}) ⇒ Transaction

Returns a new instance of Transaction.



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/new_relic/agent/transaction.rb', line 96

def initialize(type=:controller, options={})
  @type = type
  @start_time = Time.now
  @apdex_start = @start_time
  @jruby_cpu_start = jruby_cpu_time
  @process_cpu_start = process_cpu
  @filtered_params = options[:filtered_params] || {}
  @force_flag = options[:force]
  @request = options[:request]
  @exceptions = {}
  @stats_hash = StatsHash.new
  TransactionInfo.get.transaction = self
end

Instance Attribute Details

#apdex_startObject

A Time instance used for calculating the apdex score, which



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

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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

def database_metric_name
  @database_metric_name
end

#depthObject (readonly)

Returns the value of attribute depth.



94
95
96
# File 'lib/new_relic/agent/transaction.rb', line 94

def depth
  @depth
end

#exceptionsObject

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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

def exceptions
  @exceptions
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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

def jruby_cpu_start
  @jruby_cpu_start
end

#nameObject

Returns the value of attribute name.



25
26
27
# File 'lib/new_relic/agent/transaction.rb', line 25

def name
  @name
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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

def process_cpu_start
  @process_cpu_start
end

#requestObject

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



31
32
33
# File 'lib/new_relic/agent/transaction.rb', line 31

def request
  @request
end

#start_timeObject

A Time instance for the start time, never nil



19
20
21
# File 'lib/new_relic/agent/transaction.rb', line 19

def start_time
  @start_time
end

#stats_hashObject (readonly)

Returns the value of attribute stats_hash.



26
27
28
# File 'lib/new_relic/agent/transaction.rb', line 26

def stats_hash
  @stats_hash
end

#typeObject

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



23
24
25
# File 'lib/new_relic/agent/transaction.rb', line 23

def type
  @type
end

Class Method Details

.abort_transaction!Object

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



154
155
156
# File 'lib/new_relic/agent/transaction.rb', line 154

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

.add_custom_parameters(p) ⇒ Object

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



271
272
273
# File 'lib/new_relic/agent/transaction.rb', line 271

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

.agentObject



74
75
76
# File 'lib/new_relic/agent/transaction.rb', line 74

def self.agent
  NewRelic::Agent.instance
end

.apdex_bucket(duration, failed, apdex_t) ⇒ Object



385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/new_relic/agent/transaction.rb', line 385

def self.apdex_bucket(duration, failed, apdex_t)
  case
  when failed
    :apdex_f
  when duration <= apdex_t
    :apdex_s
  when duration <= 4 * apdex_t
    :apdex_t
  else
    :apdex_f
  end
end

.currentObject

Return the currently active transaction, or nil.



35
36
37
# File 'lib/new_relic/agent/transaction.rb', line 35

def self.current
  self.stack.last
end

.custom_parametersObject



275
276
277
# File 'lib/new_relic/agent/transaction.rb', line 275

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.



66
67
68
# File 'lib/new_relic/agent/transaction.rb', line 66

def self.database_metric_name
  current && current.database_metric_name
end

.freeze_nameObject



78
79
80
# File 'lib/new_relic/agent/transaction.rb', line 78

def self.freeze_name
  self.current && self.current.freeze_name
end

.in_transaction?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/new_relic/agent/transaction.rb', line 60

def self.in_transaction?
  !self.stack.empty?
end

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

If we have an active transaction, 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



244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/new_relic/agent/transaction.rb', line 244

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

.parentObject



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

def self.parent
  self.stack[-2]
end

.record_apdex(end_time, is_error) ⇒ Object



381
382
383
# File 'lib/new_relic/agent/transaction.rb', line 381

def self.record_apdex(end_time, is_error)
  current && current.record_apdex(end_time, is_error)
end

.recording_web_transaction?Boolean

Returns truthy if the current in-progress transaction is considered a a web transaction (as opposed to, e.g., a background transaction).

Returns:

  • (Boolean)


351
352
353
# File 'lib/new_relic/agent/transaction.rb', line 351

def self.recording_web_transaction?
  self.current && self.current.recording_web_transaction?
end

.refererObject



70
71
72
# File 'lib/new_relic/agent/transaction.rb', line 70

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.



361
362
363
364
365
# File 'lib/new_relic/agent/transaction.rb', line 361

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



279
280
281
# File 'lib/new_relic/agent/transaction.rb', line 279

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

.stackObject



56
57
58
# File 'lib/new_relic/agent/transaction.rb', line 56

def self.stack
  Thread.current[:newrelic_transaction] ||= []
end

.start(transaction_type, options = {}) ⇒ Object



43
44
45
46
47
48
# File 'lib/new_relic/agent/transaction.rb', line 43

def self.start(transaction_type, options={})
  txn = Transaction.new(transaction_type, options)
  txn.start(transaction_type)
  self.stack.push(txn)
  return txn
end

.stop(metric_name = nil, end_time = Time.now) ⇒ Object



50
51
52
53
54
# File 'lib/new_relic/agent/transaction.rb', line 50

def self.stop(metric_name=nil, end_time=Time.now)
  txn = self.stack.last
  txn.stop(metric_name, end_time) if txn
  return self.stack.pop
end

.uri_from_request(request) ⇒ Object

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



368
369
370
371
372
373
374
375
376
377
# File 'lib/new_relic/agent/transaction.rb', line 368

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



283
284
285
# File 'lib/new_relic/agent/transaction.rb', line 283

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



169
170
171
# File 'lib/new_relic/agent/transaction.rb', line 169

def abort_transaction!
  transaction_sampler.ignore_transaction
end

#add_custom_parameters(p) ⇒ Object



341
342
343
# File 'lib/new_relic/agent/transaction.rb', line 341

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

#custom_parametersObject



329
330
331
# File 'lib/new_relic/agent/transaction.rb', line 329

def custom_parameters
  @custom_parameters ||= {}
end

#freeze_nameObject



118
119
120
121
122
# File 'lib/new_relic/agent/transaction.rb', line 118

def freeze_name
  return if name_frozen?
  @name = NewRelic::Agent.instance.transaction_rules.rename(@name)
  @name_frozen = true
end

#has_parent?Boolean

Returns:

  • (Boolean)


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

def has_parent?
  self.class.stack.size > 1
end

#merge_stats_hashObject



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

def merge_stats_hash
  stats_hash.resolve_scopes!(@name)
  NewRelic::Agent.instance.stats_engine.merge!(stats_hash)
end

#name_frozen?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/new_relic/agent/transaction.rb', line 124

def name_frozen?
  @name_frozen
end

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

Do not call this. Invoke the class method instead.



258
259
260
261
262
263
264
265
266
267
# File 'lib/new_relic/agent/transaction.rb', line 258

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.merge!(custom_parameters)
  if !@exceptions.keys.include?(e)
    @exceptions[e] = options
  end
end

#parentObject



128
129
130
# File 'lib/new_relic/agent/transaction.rb', line 128

def parent
  has_parent? && self.class.stack[-2]
end

#queue_timeObject



337
338
339
# File 'lib/new_relic/agent/transaction.rb', line 337

def queue_time
  @apdex_start ? @start_time - @apdex_start : 0
end

#record_apdex(end_time = Time.now, is_error = nil) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/new_relic/agent/transaction.rb', line 289

def record_apdex(end_time=Time.now, is_error=nil)
  return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?

  freeze_name
  apdex_t = TransactionInfo.get.apdex_t
  action_duration = end_time - start_time
  total_duration  = end_time - apdex_start
  is_error = is_error.nil? ? !exceptions.empty? : is_error

  apdex_bucket_global = self.class.apdex_bucket(total_duration,  is_error, apdex_t)
  apdex_bucket_txn    = self.class.apdex_bucket(action_duration, is_error, apdex_t)

  @stats_hash.record(APDEX_METRIC_SPEC, apdex_bucket_global, apdex_t)
  txn_apdex_metric = NewRelic::MetricSpec.new(@name.gsub(/^[^\/]+\//, 'Apdex/'))
  @stats_hash.record(txn_apdex_metric, apdex_bucket_txn, apdex_t)
end

#record_exceptionsObject



209
210
211
212
213
214
# File 'lib/new_relic/agent/transaction.rb', line 209

def record_exceptions
  @exceptions.each do |exception, options|
    options[:metric] = @name
    agent.error_collector.notice_error(exception, options)
  end
end

#recording_web_transaction?Boolean

Returns:

  • (Boolean)


355
356
357
# File 'lib/new_relic/agent/transaction.rb', line 355

def recording_web_transaction?
  [:controller, :uri, :rack, :sinatra].include?(@type)
end

#refererObject

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



164
165
166
# File 'lib/new_relic/agent/transaction.rb', line 164

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

#root?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/new_relic/agent/transaction.rb', line 132

def root?
  self.class.stack.size == 1
end

#set_user_attributes(attributes) ⇒ Object



345
346
347
# File 'lib/new_relic/agent/transaction.rb', line 345

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

#start(transaction_type) ⇒ Object

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



142
143
144
145
146
147
148
149
150
# File 'lib/new_relic/agent/transaction.rb', line 142

def start(transaction_type)
  transaction_sampler.notice_first_scope_push(start_time)
  sql_sampler.notice_first_scope_push(start_time)

  NewRelic::Agent::StatsEngine::GCProfiler.init
  agent.stats_engine.start_transaction
  transaction_sampler.notice_transaction(uri, filtered_params)
  sql_sampler.notice_transaction(uri, filtered_params)
end

#stop(metric = ::NewRelic::Agent::UNKNOWN_METRIC, end_time = Time.now) ⇒ Object

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



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/new_relic/agent/transaction.rb', line 176

def stop(metric=::NewRelic::Agent::UNKNOWN_METRIC, end_time=Time.now)
  @name ||= metric unless name_frozen?
  freeze_name
  log_underflow if @type.nil?

  # these record metrics so need to be done before merging stats
  if self.root?
    # this one records metrics and wants to happen
    # before the transaction sampler is finished
    if traced?
      record_transaction_cpu
      gc_time = NewRelic::Agent::StatsEngine::GCProfiler.capture
    end
    @transaction_trace = transaction_sampler.notice_scope_empty(self, Time.now, gc_time)
    sql_sampler.notice_scope_empty(@name)
    overview_metrics = transaction_overview_metrics
  end

  record_exceptions
  merge_stats_hash

  # these tear everything down so need to be done after merging stats
  if self.root?
    agent.events.notify(:transaction_finished, @name, end_time.to_f - start_time.to_f, overview_metrics)
    agent.stats_engine.end_transaction
  end
end

#transaction_overview_metricsObject



225
226
227
228
229
230
231
232
# File 'lib/new_relic/agent/transaction.rb', line 225

def transaction_overview_metrics
  metrics = {}
  stats = @stats_hash
  OVERVIEW_SPECS.each do |(dest_key, spec)|
    metrics[dest_key] = stats[spec].total_call_time if stats.key?(spec)
  end
  metrics
end

#uriObject

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



159
160
161
# File 'lib/new_relic/agent/transaction.rb', line 159

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

#user_attributesObject



333
334
335
# File 'lib/new_relic/agent/transaction.rb', line 333

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.



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/new_relic/agent/transaction.rb', line 313

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