Class: Appsignal::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/appsignal/transaction.rb

Defined Under Namespace

Classes: GenericRequest, NilTransaction

Constant Summary collapse

HTTP_REQUEST =
"http_request"
BACKGROUND_JOB =
"background_job"
ACTION_CABLE =
"action_cable"
FRONTEND =
"frontend"
BLANK =
""
ALLOWED_TAG_KEY_TYPES =
[Symbol, String].freeze
ALLOWED_TAG_VALUE_TYPES =
[Symbol, String, Integer].freeze
20
ERROR_CAUSES_LIMIT =
10

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transaction_id, namespace, request, options = {}) ⇒ Transaction

Returns a new instance of Transaction.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/appsignal/transaction.rb', line 91

def initialize(transaction_id, namespace, request, options = {})
  @transaction_id = transaction_id
  @action = nil
  @namespace = namespace
  @request = request
  @paused = false
  @discarded = false
  @tags = {}
  @breadcrumbs = []
  @store = Hash.new({})
  @options = options
  @options[:params_method] ||= :params

  @ext = Appsignal::Extension.start_transaction(
    @transaction_id,
    @namespace,
    0
  ) || Appsignal::Extension::MockTransaction.new
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def action
  @action
end

Returns the value of attribute breadcrumbs.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def breadcrumbs
  @breadcrumbs
end

#discardedObject (readonly)

Returns the value of attribute discarded.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def discarded
  @discarded
end

#extObject (readonly)

Returns the value of attribute ext.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def ext
  @ext
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def namespace
  @namespace
end

#optionsObject (readonly)

Returns the value of attribute options.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def options
  @options
end

#paramsHash

Attribute for parameters of the transaction.

When no parameters are set with #params= the parameters it will look for parameters on the #request environment.

The parameters set using #params= are leading over those extracted from a request's environment.

Returns:

  • (Hash)


89
# File 'lib/appsignal/transaction.rb', line 89

attr_writer :params

#pausedObject (readonly)

Returns the value of attribute paused.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def paused
  @paused
end

#requestObject (readonly)

Returns the value of attribute request.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def request
  @request
end

#tagsObject (readonly)

Returns the value of attribute tags.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def tags
  @tags
end

#transaction_idObject (readonly)

Returns the value of attribute transaction_id.



76
77
78
# File 'lib/appsignal/transaction.rb', line 76

def transaction_id
  @transaction_id
end

Class Method Details

.clear_current_transaction!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Remove current transaction from current Thread.



71
72
73
# File 'lib/appsignal/transaction.rb', line 71

def clear_current_transaction!
  Thread.current[:appsignal_transaction] = nil
end

.complete_current!Object



59
60
61
62
63
64
65
66
67
# File 'lib/appsignal/transaction.rb', line 59

def complete_current!
  current.complete
rescue => e
  Appsignal.internal_logger.error(
    "Failed to complete transaction ##{current.transaction_id}. #{e.message}"
  )
ensure
  clear_current_transaction!
end

.create(id, namespace, request, options = {}) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/appsignal/transaction.rb', line 18

def create(id, namespace, request, options = {})
  # Allow middleware to force a new transaction
  Thread.current[:appsignal_transaction] = nil if options.include?(:force) && options[:force]

  # Check if we already have a running transaction
  if Thread.current[:appsignal_transaction].nil?
    # If not, start a new transaction
    Thread.current[:appsignal_transaction] =
      Appsignal::Transaction.new(id, namespace, request, options)
  else
    # Otherwise, log the issue about trying to start another transaction
    Appsignal.internal_logger.warn_once_then_debug(
      :transaction_id,
      "Trying to start new transaction with id " \
        "'#{id}', but a transaction with id '#{current.transaction_id}' " \
        "is already running. Using transaction '#{current.transaction_id}'."
    )

    # And return the current transaction instead
    current
  end
end

.currentBoolean

Returns currently active transaction or a NilTransaction if none is active.

Returns:

  • (Boolean)

See Also:



46
47
48
# File 'lib/appsignal/transaction.rb', line 46

def current
  Thread.current[:appsignal_transaction] || NilTransaction.new
end

.current?Boolean

Returns if any transaction is currently active or not. A NilTransaction is not considered an active transaction.

Returns:

  • (Boolean)

See Also:



55
56
57
# File 'lib/appsignal/transaction.rb', line 55

def current?
  current && !current.nil_transaction?
end

Instance Method Details

#add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc) ⇒ void

This method returns an undefined value.

Add breadcrumbs to the transaction.

Parameters:

  • category (String)

    category of breadcrumb e.g. "UI", "Network", "Navigation", "Console".

  • action (String)

    name of breadcrumb e.g "The user clicked a button", "HTTP 500 from http://blablabla.com"

  • message (Hash) (defaults to: "")

    a customizable set of options

  • metadata (Hash) (defaults to: {})

    a customizable set of options

  • time (Hash) (defaults to: Time.now.utc)

    a customizable set of options

Options Hash (message):

  • optional (String)

    message in string format

Options Hash (metadata):

  • key/value (Hash<String,String>)

    metadata in format

Options Hash (time):

  • time (Time)

    of breadcrumb, should respond to .to_i defaults to Time.now.utc

See Also:



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/appsignal/transaction.rb', line 189

def add_breadcrumb(category, action, message = "",  = {}, time = Time.now.utc)
  unless .is_a? Hash
    Appsignal.internal_logger.error "add_breadcrumb: Cannot add breadcrumb. " \
      "The given metadata argument is not a Hash."
    return
  end

  @breadcrumbs.push(
    :time => time.to_i,
    :category => category,
    :action => action,
    :message => message,
    :metadata => 
  )
  @breadcrumbs = @breadcrumbs.last(BREADCRUMB_LIMIT)
end

#completeObject



115
116
117
118
119
120
121
122
123
# File 'lib/appsignal/transaction.rb', line 115

def complete
  if discarded?
    Appsignal.internal_logger.debug "Skipping transaction '#{transaction_id}' " \
      "because it was manually discarded."
    return
  end
  sample_data if @ext.finish(0)
  @ext.complete
end

#discard!Object



137
138
139
# File 'lib/appsignal/transaction.rb', line 137

def discard!
  @discarded = true
end

#discarded?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/appsignal/transaction.rb', line 145

def discarded?
  @discarded == true
end

#finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT) ⇒ Object



422
423
424
425
426
427
428
429
430
431
432
# File 'lib/appsignal/transaction.rb', line 422

def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
  return if paused?

  @ext.finish_event(
    name,
    title || BLANK,
    body || BLANK,
    body_format || Appsignal::EventFormatter::DEFAULT,
    0
  )
end

#instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT) ⇒ Object



447
448
449
450
451
452
# File 'lib/appsignal/transaction.rb', line 447

def instrument(name, title = nil, body = nil, body_format = Appsignal::EventFormatter::DEFAULT)
  start_event
  yield if block_given?
ensure
  finish_event(name, title, body, body_format)
end

#nil_transaction?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/appsignal/transaction.rb', line 111

def nil_transaction?
  false
end

#pause!Object



125
126
127
# File 'lib/appsignal/transaction.rb', line 125

def pause!
  @paused = true
end

#paused?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/appsignal/transaction.rb', line 133

def paused?
  @paused == true
end

#record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT) ⇒ Object



434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/appsignal/transaction.rb', line 434

def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
  return if paused?

  @ext.record_event(
    name,
    title || BLANK,
    body || BLANK,
    body_format || Appsignal::EventFormatter::DEFAULT,
    duration,
    0
  )
end

#restore!Object



141
142
143
# File 'lib/appsignal/transaction.rb', line 141

def restore!
  @discarded = false
end

#resume!Object



129
130
131
# File 'lib/appsignal/transaction.rb', line 129

def resume!
  @paused = false
end

#sample_dataObject



350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/appsignal/transaction.rb', line 350

def sample_data
  {
    :params => sanitized_params,
    :environment => sanitized_environment,
    :session_data => sanitized_session_data,
    :metadata => ,
    :tags => sanitized_tags,
    :breadcrumbs => breadcrumbs
  }.each do |key, data|
    set_sample_data(key, data)
  end
end

#set_action(action) ⇒ void

This method returns an undefined value.

Set an action name for the transaction.

An action name is used to identify the location of a certain sample; error and performance issues.

Parameters:

  • action (String)

    the action name to set.

See Also:

Since:

  • 2.2.0



216
217
218
219
220
221
# File 'lib/appsignal/transaction.rb', line 216

def set_action(action)
  return unless action

  @action = action
  @ext.set_action(action)
end

#set_action_if_nil(action) ⇒ void

This method returns an undefined value.

Set an action name only if there is no current action set.

Commonly used by AppSignal integrations so that they don't override custom action names.

Examples:

Appsignal.set_action("foo")
Appsignal.set_action_if_nil("bar")
# Transaction action will be "foo"

Parameters:

  • action (String)

See Also:

Since:

  • 2.2.0



237
238
239
240
241
# File 'lib/appsignal/transaction.rb', line 237

def set_action_if_nil(action)
  return if @action

  set_action(action)
end

#set_error(error) ⇒ Object Also known as: add_exception



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/appsignal/transaction.rb', line 363

def set_error(error)
  unless error.is_a?(Exception)
    Appsignal.internal_logger.error "Appsignal::Transaction#set_error: Cannot set error. " \
      "The given value is not an exception: #{error.inspect}"
    return
  end
  return unless error
  return unless Appsignal.active?

  backtrace = cleaned_backtrace(error.backtrace)
  @ext.set_error(
    error.class.name,
    cleaned_error_message(error),
    backtrace ? Appsignal::Utils::Data.generate(backtrace) : Appsignal::Extension.data_array_new
  )

  root_cause_missing = false

  causes = []
  while error
    error = error.cause

    break unless error

    if causes.length >= ERROR_CAUSES_LIMIT
      Appsignal.internal_logger.debug "Appsignal::Transaction#set_error: Error has more " \
        "than #{ERROR_CAUSES_LIMIT} error causes. Only the first #{ERROR_CAUSES_LIMIT} " \
        "will be reported."
      root_cause_missing = true
      break
    end

    causes << error
  end

  return if causes.empty?

  causes_sample_data = causes.map do |e|
    {
      :name => e.class.name,
      :message => cleaned_error_message(e)
    }
  end

  causes_sample_data.last[:is_root_cause] = false if root_cause_missing

  set_sample_data(
    "error_causes",
    causes_sample_data
  )
end

#set_http_or_background_action(from = request.params) ⇒ Object



265
266
267
268
269
270
271
272
273
# File 'lib/appsignal/transaction.rb', line 265

def set_http_or_background_action(from = request.params)
  return unless from

  group_and_action = [
    from[:controller] || from[:class],
    from[:action] || from[:method]
  ]
  set_action_if_nil(group_and_action.compact.join("#"))
end

#set_http_or_background_queue_startvoid

This method returns an undefined value.

Set the queue time based on the HTTP header or :queue_start env key value.

This method will first try to read the queue time from the HTTP headers X-Request-Start or X-Queue-Start. Which are parsed by Rack as HTTP_X_QUEUE_START and HTTP_X_REQUEST_START. The header value is parsed by AppSignal as either milliseconds or microseconds.

If no headers are found, or the value could not be parsed, it falls back on the :queue_start env key on this Transaction's #request environment (called like request.env[:queue_start]). This value is parsed by AppSignal as seconds.



309
310
311
312
313
314
# File 'lib/appsignal/transaction.rb', line 309

def set_http_or_background_queue_start
  start = http_queue_start || background_queue_start
  return unless start

  set_queue_start(start)
end

#set_metadata(key, value) ⇒ Object



316
317
318
319
320
321
# File 'lib/appsignal/transaction.rb', line 316

def (key, value)
  return unless key && value
  return if Appsignal.config[:filter_metadata].include?(key.to_s)

  @ext.(key, value)
end

#set_namespace(namespace) ⇒ void

This method returns an undefined value.

Set the namespace for this transaction.

Useful to split up parts of an application into certain namespaces. For example: http requests, background jobs and administration panel controllers.

Note: The "http_request" namespace gets transformed on AppSignal.com to "Web" and "background_job" gets transformed to "Background".

Examples:

transaction.set_namespace("background")

Parameters:

  • namespace (String)

    namespace name to use for this transaction.

Since:

  • 2.2.0



258
259
260
261
262
263
# File 'lib/appsignal/transaction.rb', line 258

def set_namespace(namespace)
  return unless namespace

  @namespace = namespace
  @ext.set_namespace(namespace)
end

#set_queue_start(start) ⇒ void

This method returns an undefined value.

Set queue start time for transaction.

Most commononly called by #set_http_or_background_queue_start.

Parameters:

  • start (Integer)

    Queue start time in milliseconds.

Raises:

  • (RangeError)

    When the queue start time value is too big, this method raises a RangeError.

  • (TypeError)

    Raises a TypeError when the given start argument is not an Integer.



285
286
287
288
289
290
291
# File 'lib/appsignal/transaction.rb', line 285

def set_queue_start(start)
  return unless start

  @ext.set_queue_start(start)
rescue RangeError
  Appsignal.internal_logger.warn("Queue start value #{start} is too big")
end

#set_sample_data(key, data) ⇒ Object



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/appsignal/transaction.rb', line 323

def set_sample_data(key, data)
  return unless key && data

  if !data.is_a?(Array) && !data.is_a?(Hash)
    Appsignal.internal_logger.error(
      "Invalid sample data for '#{key}'. Value is not an Array or Hash: '#{data.inspect}'"
    )
    return
  end

  @ext.set_sample_data(
    key.to_s,
    Appsignal::Utils::Data.generate(data)
  )
rescue RuntimeError => e
  begin
    inspected_data = data.inspect
    Appsignal.internal_logger.error(
      "Error generating data (#{e.class}: #{e.message}) for '#{inspected_data}'"
    )
  rescue => e
    Appsignal.internal_logger.error(
      "Error generating data (#{e.class}: #{e.message}). Can't inspect data."
    )
  end
end

#set_tags(given_tags = {}) ⇒ void

This method returns an undefined value.

Set tags on the transaction.

Parameters:

  • given_tags (Hash) (defaults to: {})

    Collection of tags.

Options Hash (given_tags):

  • :any (String, Symbol, Integer)

    The name of the tag as a Symbol.

  • "any" (String, Symbol, Integer)

    The name of the tag as a String.

See Also:



171
172
173
# File 'lib/appsignal/transaction.rb', line 171

def set_tags(given_tags = {})
  @tags.merge!(given_tags)
end

#start_eventObject



416
417
418
419
420
# File 'lib/appsignal/transaction.rb', line 416

def start_event
  return if paused?

  @ext.start_event(0)
end

#store(key) ⇒ Object



149
150
151
# File 'lib/appsignal/transaction.rb', line 149

def store(key)
  @store[key]
end

#to_hObject Also known as: to_hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



455
456
457
# File 'lib/appsignal/transaction.rb', line 455

def to_h
  JSON.parse(@ext.to_json)
end