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".freeze
BACKGROUND_JOB =
"background_job".freeze
ACTION_CABLE =
"action_cable".freeze
FRONTEND =
"frontend".freeze
BLANK =
"".freeze
ENV_METHODS =

Based on what Rails uses + some variables we'd like to show

%w[
  CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
  PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
  REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL REQUEST_URI
  PATH_INFO

  HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
  HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
  HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
  HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM
  HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR
  HTTP_CLIENT_IP HTTP_RANGE
].freeze

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.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/appsignal/transaction.rb', line 85

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

  @ext = Appsignal::Extension.start_transaction(
    @transaction_id,
    @namespace,
    self.class.garbage_collection_profiler.total_time
  )
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action.



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

def action
  @action
end

#discardedObject (readonly)

Returns the value of attribute discarded.



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

def discarded
  @discarded
end

#extObject (readonly)

Returns the value of attribute ext.



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

def ext
  @ext
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



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

def namespace
  @namespace
end

#optionsObject (readonly)

Returns the value of attribute options.



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

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)


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

attr_writer :params

#pausedObject (readonly)

Returns the value of attribute paused.



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

def paused
  @paused
end

#requestObject (readonly)

Returns the value of attribute request.



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

def request
  @request
end

#tagsObject (readonly)

Returns the value of attribute tags.



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

def tags
  @tags
end

#transaction_idObject (readonly)

Returns the value of attribute transaction_id.



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

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.



62
63
64
# File 'lib/appsignal/transaction.rb', line 62

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

.complete_current!Object



52
53
54
55
56
57
58
# File 'lib/appsignal/transaction.rb', line 52

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

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



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/appsignal/transaction.rb', line 27

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

  # Check if we already have a running transaction
  if Thread.current[:appsignal_transaction] != nil
    # Log the issue and return the current transaction
    Appsignal.logger.debug "Trying to start new transaction with id " \
      "'#{id}', but a transaction with id '#{current.transaction_id}' " \
      "is already running. Using transaction '#{current.transaction_id}'."

    # Return the current (running) transaction
    current
  else
    # Otherwise, start a new transaction
    Thread.current[:appsignal_transaction] = Appsignal::Transaction.new(id, namespace, request, options)
  end
end

.currentObject



48
49
50
# File 'lib/appsignal/transaction.rb', line 48

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

.garbage_collection_profilerObject



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

def garbage_collection_profiler
  @garbage_collection_profiler ||= Appsignal::GarbageCollectionProfiler.new
end

Instance Method Details

#completeObject



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/appsignal/transaction.rb', line 108

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

#discard!Object



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

def discard!
  @discarded = true
end

#discarded?Boolean

Returns:

  • (Boolean)


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

def discarded?
  @discarded == true
end

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



293
294
295
296
297
298
299
300
301
# File 'lib/appsignal/transaction.rb', line 293

def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
  @ext.finish_event(
    name,
    title || BLANK,
    body || BLANK,
    body_format || Appsignal::EventFormatter::DEFAULT,
    self.class.garbage_collection_profiler.total_time
  )
end

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



314
315
316
317
318
319
# File 'lib/appsignal/transaction.rb', line 314

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)


104
105
106
# File 'lib/appsignal/transaction.rb', line 104

def nil_transaction?
  false
end

#pause!Object



120
121
122
# File 'lib/appsignal/transaction.rb', line 120

def pause!
  @paused = true
end

#paused?Boolean

Returns:

  • (Boolean)


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

def paused?
  @paused == true
end

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



303
304
305
306
307
308
309
310
311
312
# File 'lib/appsignal/transaction.rb', line 303

def record_event(name, title, body, duration, body_format = Appsignal::EventFormatter::DEFAULT)
  @ext.record_event(
    name,
    title || BLANK,
    body || BLANK,
    body_format || Appsignal::EventFormatter::DEFAULT,
    duration,
    self.class.garbage_collection_profiler.total_time
  )
end

#restore!Object



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

def restore!
  @discarded = false
end

#resume!Object



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

def resume!
  @paused = false
end

#sample_dataObject



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

def sample_data
  {
    :params       => sanitized_params,
    :environment  => sanitized_environment,
    :session_data => sanitized_session_data,
    :metadata     => ,
    :tags         => sanitized_tags
  }.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



179
180
181
182
183
# File 'lib/appsignal/transaction.rb', line 179

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



199
200
201
202
# File 'lib/appsignal/transaction.rb', line 199

def set_action_if_nil(action)
  return if @action
  set_action(action)
end

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



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/appsignal/transaction.rb', line 276

def set_error(error)
  return unless error
  return unless Appsignal.active?

  backtrace = cleaned_backtrace(error.backtrace)
  @ext.set_error(
    error.class.name,
    error.message.to_s,
    backtrace ? Appsignal::Utils.data_generate(backtrace) : Appsignal::Extension.data_array_new
  )
end

#set_http_or_background_action(from = request.params) ⇒ Object



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

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(group_and_action.compact.join("#"))
end

#set_http_or_background_queue_startObject



241
242
243
244
245
246
247
# File 'lib/appsignal/transaction.rb', line 241

def set_http_or_background_queue_start
  if namespace == HTTP_REQUEST
    set_queue_start(http_queue_start)
  elsif namespace == BACKGROUND_JOB
    set_queue_start(background_queue_start)
  end
end

#set_metadata(key, value) ⇒ Object



249
250
251
252
# File 'lib/appsignal/transaction.rb', line 249

def (key, value)
  return unless key && value
  @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_action("admin")

Parameters:

  • namespace (String)

    namespace name to use for this transaction.

Since:

  • 2.2.0



219
220
221
222
223
# File 'lib/appsignal/transaction.rb', line 219

def set_namespace(namespace)
  return unless namespace
  @namespace = namespace
  @ext.set_namespace(namespace)
end

#set_queue_start(start) ⇒ Object



234
235
236
237
238
239
# File 'lib/appsignal/transaction.rb', line 234

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

#set_sample_data(key, data) ⇒ Object



254
255
256
257
258
259
260
261
262
# File 'lib/appsignal/transaction.rb', line 254

def set_sample_data(key, data)
  return unless key && data && (data.is_a?(Array) || data.is_a?(Hash))
  @ext.set_sample_data(
    key.to_s,
    Appsignal::Utils.data_generate(data)
  )
rescue RuntimeError => e
  Appsignal.logger.error("Error generating data (#{e.class}: #{e.message}) for '#{data.inspect}'")
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:



165
166
167
# File 'lib/appsignal/transaction.rb', line 165

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

#start_eventObject



289
290
291
# File 'lib/appsignal/transaction.rb', line 289

def start_event
  @ext.start_event(self.class.garbage_collection_profiler.total_time)
end

#store(key) ⇒ Object



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

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.



322
323
324
# File 'lib/appsignal/transaction.rb', line 322

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