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



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/appsignal/transaction.rb', line 64

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.



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

def action
  @action
end

#discardedObject (readonly)

Returns the value of attribute discarded.



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

def discarded
  @discarded
end

#extObject (readonly)

Returns the value of attribute ext.



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

def ext
  @ext
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



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

def namespace
  @namespace
end

#optionsObject (readonly)

Returns the value of attribute options.



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

def options
  @options
end

#pausedObject (readonly)

Returns the value of attribute paused.



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

def paused
  @paused
end

#requestObject (readonly)

Returns the value of attribute request.



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

def request
  @request
end

#tagsObject (readonly)

Returns the value of attribute tags.



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

def tags
  @tags
end

#transaction_idObject (readonly)

Returns the value of attribute transaction_id.



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

def transaction_id
  @transaction_id
end

Class Method Details

.complete_current!Object



49
50
51
52
53
54
55
# File 'lib/appsignal/transaction.rb', line 49

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



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

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 #{id} but #{current.transaction_id} is already running. Using #{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



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

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

.garbage_collection_profilerObject



57
58
59
# File 'lib/appsignal/transaction.rb', line 57

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

Instance Method Details

#completeObject



87
88
89
90
91
92
93
94
95
96
# File 'lib/appsignal/transaction.rb', line 87

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

#discard!Object



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

def discard!
  @discarded = true
end

#discarded?Boolean

Returns:

  • (Boolean)


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

def discarded?
  @discarded == true
end

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



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

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



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

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)


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

def nil_transaction?
  false
end

#pause!Object



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

def pause!
  @paused = true
end

#paused?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/appsignal/transaction.rb', line 106

def paused?
  @paused == true
end

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



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

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



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

def restore!
  @discarded = false
end

#resume!Object



102
103
104
# File 'lib/appsignal/transaction.rb', line 102

def resume!
  @paused = false
end

#sample_dataObject



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

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



152
153
154
155
156
# File 'lib/appsignal/transaction.rb', line 152

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



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

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

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



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

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



198
199
200
201
202
203
204
205
# File 'lib/appsignal/transaction.rb', line 198

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



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

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



222
223
224
225
# File 'lib/appsignal/transaction.rb', line 222

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



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

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

#set_queue_start(start) ⇒ Object



207
208
209
210
211
212
# File 'lib/appsignal/transaction.rb', line 207

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



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

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:



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

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

#start_eventObject



262
263
264
# File 'lib/appsignal/transaction.rb', line 262

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

#store(key) ⇒ Object



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

def store(key)
  @store[key]
end