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 HTTP_X_AUTH_TOKEN
).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.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/appsignal/transaction.rb', line 79

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.



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

def action
  @action
end

#discardedObject (readonly)

Returns the value of attribute discarded.



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

def discarded
  @discarded
end

#extObject (readonly)

Returns the value of attribute ext.



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

def ext
  @ext
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



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

def namespace
  @namespace
end

#optionsObject (readonly)

Returns the value of attribute options.



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

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)


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

attr_writer :params

#pausedObject (readonly)

Returns the value of attribute paused.



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

def paused
  @paused
end

#requestObject (readonly)

Returns the value of attribute request.



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

def request
  @request
end

#tagsObject (readonly)

Returns the value of attribute tags.



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

def tags
  @tags
end

#transaction_idObject (readonly)

Returns the value of attribute transaction_id.



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

def transaction_id
  @transaction_id
end

Class Method Details

.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
  Thread.current[:appsignal_transaction] = nil
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



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

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

Instance Method Details

#completeObject



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/appsignal/transaction.rb', line 102

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



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

def discard!
  @discarded = true
end

#discarded?Boolean

Returns:

  • (Boolean)


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

def discarded?
  @discarded == true
end

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



287
288
289
290
291
292
293
294
295
# File 'lib/appsignal/transaction.rb', line 287

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



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

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)


98
99
100
# File 'lib/appsignal/transaction.rb', line 98

def nil_transaction?
  false
end

#pause!Object



114
115
116
# File 'lib/appsignal/transaction.rb', line 114

def pause!
  @paused = true
end

#paused?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/appsignal/transaction.rb', line 122

def paused?
  @paused == true
end

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



297
298
299
300
301
302
303
304
305
306
# File 'lib/appsignal/transaction.rb', line 297

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



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

def restore!
  @discarded = false
end

#resume!Object



118
119
120
# File 'lib/appsignal/transaction.rb', line 118

def resume!
  @paused = false
end

#sample_dataObject



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

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



173
174
175
176
177
# File 'lib/appsignal/transaction.rb', line 173

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



193
194
195
196
# File 'lib/appsignal/transaction.rb', line 193

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

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



270
271
272
273
274
275
276
277
278
279
280
# File 'lib/appsignal/transaction.rb', line 270

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



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

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



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

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



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

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



213
214
215
216
217
# File 'lib/appsignal/transaction.rb', line 213

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

#set_queue_start(start) ⇒ Object



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

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



248
249
250
251
252
253
254
255
256
# File 'lib/appsignal/transaction.rb', line 248

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:



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

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

#start_eventObject



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

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

#store(key) ⇒ Object



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

def store(key)
  @store[key]
end

#to_hObject Also known as: to_hash



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

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