appsignal 2.2.1 → 2.3.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/appsignal.gemspec +1 -2
  4. data/ext/agent.yml +11 -11
  5. data/ext/appsignal_extension.c +17 -1
  6. data/lib/appsignal/config.rb +7 -4
  7. data/lib/appsignal/hooks.rb +1 -6
  8. data/lib/appsignal/hooks/action_cable.rb +113 -0
  9. data/lib/appsignal/hooks/active_support_notifications.rb +12 -4
  10. data/lib/appsignal/hooks/shoryuken.rb +11 -11
  11. data/lib/appsignal/hooks/sidekiq.rb +5 -3
  12. data/lib/appsignal/integrations/delayed_job_plugin.rb +5 -1
  13. data/lib/appsignal/integrations/resque_active_job.rb +4 -1
  14. data/lib/appsignal/transaction.rb +40 -13
  15. data/lib/appsignal/version.rb +1 -1
  16. data/spec/lib/appsignal/config_spec.rb +8 -1
  17. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  18. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +39 -7
  19. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +179 -34
  20. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +125 -30
  21. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +140 -21
  22. data/spec/lib/appsignal/hooks_spec.rb +0 -21
  23. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +62 -17
  24. data/spec/lib/appsignal/integrations/resque_spec.rb +24 -12
  25. data/spec/lib/appsignal/transaction_spec.rb +230 -91
  26. data/spec/spec_helper.rb +8 -2
  27. data/spec/support/helpers/dependency_helper.rb +4 -0
  28. data/spec/support/helpers/env_helpers.rb +1 -1
  29. data/spec/support/helpers/log_helpers.rb +17 -0
  30. data/spec/support/matchers/contains_log.rb +7 -0
  31. data/spec/support/shared_examples/instrument.rb +2 -2
  32. metadata +13 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 705384b1874c28d97c9686459bcbfc6a99fb2314
4
- data.tar.gz: d191875adb5dadac78d8148828a346e9165964a4
3
+ metadata.gz: 5785ecc19ccd3550a6a654429274f796d462b6b4
4
+ data.tar.gz: ec699ff6da87026d7b003c1343d77c0ed3513914
5
5
  SHA512:
6
- metadata.gz: fd0dc31ae1795897907da34b6ce862777829e81b6e98013830caa4818bf17f77f683e4a6beb176d6664213f5e64bd602df410aedab8ac798b58432dc0417daa5
7
- data.tar.gz: 7a448280ea51c4426e6a9322f8a264fe99f94c25312dd663cdd489d8d9f9ac58d81e5f21e154fb309a9fcc035fc23fbcfc7ea98394597305ae49ad021356f167
6
+ metadata.gz: f533af191e8b214188d676db0d834d117e3c0ed4bad7d52cacb83b2a5ed78285e640a738a1fde78f9a29f0cabab97277034ec74a242b833f63236d1fe2d1bf8b
7
+ data.tar.gz: 71dac0fc78a0964425672356ac88ca2518f16c47a340408c2fafd7d15991f99df7907c34790c91dc093b2c30f1199b7705c7e9eef16004df133e36ef31e2f78a
@@ -1,3 +1,18 @@
1
+ # 2.3.0 (unreleased)
2
+ * Fix Shoryuken instrumentation when body is a string. PR #266
3
+ * Enable ActiveSupport instrumentation at all times. PR #274
4
+ * Add parameter filtering for background jobs. Automatically uses the AppSignal
5
+ parameter filtering. PR #280
6
+ * Improve log messages for transactions. PR #293
7
+ * Remove thread_safe dependency. PR #294
8
+ * Add `Transaction#params` attribute for custom parameters. PR #295
9
+ * Add ActionCable support. PR #309
10
+ * Finish ActiveSupport notifications events when they would encounter a `raise`
11
+ or a `throw`. PR #310
12
+ * Add `ignore_namespaces` option. PR #312
13
+ * Truncate lengthy parameter values to 2000 characters.
14
+ Commit 65de1382f5f453b624781cde6e0544c89fdf89ef
15
+
1
16
  # 2.2.1
2
17
  * Fix support for Rails 5.1. PR #286
3
18
  * Fix instrumentation that would report a duration of `0ms` for all DataMapper
@@ -24,10 +24,9 @@ Gem::Specification.new do |gem|
24
24
  gem.extensions = %w(ext/extconf.rb)
25
25
 
26
26
  gem.add_dependency "rack"
27
- gem.add_dependency "thread_safe"
28
27
 
29
28
  gem.add_development_dependency "rake", "~> 11"
30
- gem.add_development_dependency "rspec", "~> 3.5"
29
+ gem.add_development_dependency "rspec", "~> 3.6"
31
30
  gem.add_development_dependency "pry"
32
31
  gem.add_development_dependency "timecop"
33
32
  gem.add_development_dependency "webmock"
@@ -1,18 +1,18 @@
1
1
  ---
2
- version: d54a76a
2
+ version: d5b0750
3
3
  triples:
4
4
  x86_64-linux:
5
- checksum: 281d91b060365e05fc2df94eb88b0b8d7f6fde06c439829bdd170f202f5aa5b1
6
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d54a76a/appsignal-x86_64-linux-all-static.tar.gz
5
+ checksum: 1ff68355e089d855927dfd5058e635bf1ad3196f8f9c6f4577ac6854bca133bf
6
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d5b0750/appsignal-x86_64-linux-all-static.tar.gz
7
7
  i686-linux:
8
- checksum: 91e9d317a45d0a5e1a03b5a12480c221182b8f460638532181b58b362941d3d5
9
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d54a76a/appsignal-i686-linux-all-static.tar.gz
8
+ checksum: fcfa0d7a0ebd628d24ba799e0c765210ee2c93d1eff6930b94fd9ca4383cb278
9
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d5b0750/appsignal-i686-linux-all-static.tar.gz
10
10
  x86-linux:
11
- checksum: 91e9d317a45d0a5e1a03b5a12480c221182b8f460638532181b58b362941d3d5
12
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d54a76a/appsignal-i686-linux-all-static.tar.gz
11
+ checksum: fcfa0d7a0ebd628d24ba799e0c765210ee2c93d1eff6930b94fd9ca4383cb278
12
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d5b0750/appsignal-i686-linux-all-static.tar.gz
13
13
  x86_64-darwin:
14
- checksum: f9732af3f4913341d0ec70474d893472fd1bf9720c5cea098a24be92379872aa
15
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d54a76a/appsignal-x86_64-darwin-all-static.tar.gz
14
+ checksum: 9e394fa5f473f81ef4f322b3802aeee2b08854ea43a75f9c22c6185b4ffd960a
15
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d5b0750/appsignal-x86_64-darwin-all-static.tar.gz
16
16
  universal-darwin:
17
- checksum: f9732af3f4913341d0ec70474d893472fd1bf9720c5cea098a24be92379872aa
18
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d54a76a/appsignal-x86_64-darwin-all-static.tar.gz
17
+ checksum: 9e394fa5f473f81ef4f322b3802aeee2b08854ea43a75f9c22c6185b4ffd960a
18
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d5b0750/appsignal-x86_64-darwin-all-static.tar.gz
@@ -281,6 +281,21 @@ static VALUE complete_transaction(VALUE self) {
281
281
  return Qnil;
282
282
  }
283
283
 
284
+ static VALUE transaction_to_json(VALUE self) {
285
+ appsignal_transaction_t* transaction;
286
+ appsignal_string_t json;
287
+
288
+ Data_Get_Struct(self, appsignal_transaction_t, transaction);
289
+
290
+ json = appsignal_transaction_to_json(transaction);
291
+
292
+ if (json.len == 0) {
293
+ return Qnil;
294
+ } else {
295
+ return make_ruby_string(json);
296
+ }
297
+ }
298
+
284
299
  static VALUE data_map_new(VALUE self) {
285
300
  appsignal_data_t* data;
286
301
 
@@ -636,6 +651,7 @@ void Init_appsignal_extension(void) {
636
651
  rb_define_method(Transaction, "set_metadata", set_transaction_metadata, 2);
637
652
  rb_define_method(Transaction, "finish", finish_transaction, 1);
638
653
  rb_define_method(Transaction, "complete", complete_transaction, 0);
654
+ rb_define_method(Transaction, "to_json", transaction_to_json, 0);
639
655
 
640
656
  // Create a data map or array
641
657
  rb_define_singleton_method(Extension, "data_map_new", data_map_new, 0);
@@ -660,7 +676,7 @@ void Init_appsignal_extension(void) {
660
676
  // Data equality
661
677
  rb_define_method(Data, "==", data_equal, 1);
662
678
 
663
- // Get Json content of a data
679
+ // Get JSON content of a data
664
680
  rb_define_method(Data, "to_s", data_to_s, 0);
665
681
 
666
682
  // Event hook installation
@@ -9,8 +9,9 @@ module Appsignal
9
9
  DEFAULT_CONFIG = {
10
10
  :debug => false,
11
11
  :log => "file",
12
- :ignore_errors => [],
13
12
  :ignore_actions => [],
13
+ :ignore_errors => [],
14
+ :ignore_namespaces => [],
14
15
  :filter_parameters => [],
15
16
  :send_params => true,
16
17
  :endpoint => "https://push.appsignal.com",
@@ -43,8 +44,9 @@ module Appsignal
43
44
  "APPSIGNAL_INSTRUMENT_SEQUEL" => :instrument_sequel,
44
45
  "APPSIGNAL_SKIP_SESSION_DATA" => :skip_session_data,
45
46
  "APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING" => :enable_frontend_error_catching,
46
- "APPSIGNAL_IGNORE_ERRORS" => :ignore_errors,
47
47
  "APPSIGNAL_IGNORE_ACTIONS" => :ignore_actions,
48
+ "APPSIGNAL_IGNORE_ERRORS" => :ignore_errors,
49
+ "APPSIGNAL_IGNORE_NAMESPACES" => :ignore_namespaces,
48
50
  "APPSIGNAL_FILTER_PARAMETERS" => :filter_parameters,
49
51
  "APPSIGNAL_SEND_PARAMS" => :send_params,
50
52
  "APPSIGNAL_HTTP_PROXY" => :http_proxy,
@@ -132,6 +134,7 @@ module Appsignal
132
134
  ENV["_APPSIGNAL_HTTP_PROXY"] = config_hash[:http_proxy]
133
135
  ENV["_APPSIGNAL_IGNORE_ACTIONS"] = config_hash[:ignore_actions].join(",")
134
136
  ENV["_APPSIGNAL_IGNORE_ERRORS"] = config_hash[:ignore_errors].join(",")
137
+ ENV["_APPSIGNAL_IGNORE_NAMESPACES"] = config_hash[:ignore_namespaces].join(",")
135
138
  ENV["_APPSIGNAL_FILTER_PARAMETERS"] = config_hash[:filter_parameters].join(",")
136
139
  ENV["_APPSIGNAL_SEND_PARAMS"] = config_hash[:send_params].to_s
137
140
  ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"] = config_hash[:running_in_container].to_s
@@ -209,8 +212,8 @@ module Appsignal
209
212
  end
210
213
 
211
214
  # Configuration with array of strings type
212
- %w(APPSIGNAL_IGNORE_ERRORS APPSIGNAL_IGNORE_ACTIONS
213
- APPSIGNAL_FILTER_PARAMETERS).each do |var|
215
+ %w(APPSIGNAL_IGNORE_ACTIONS APPSIGNAL_IGNORE_ERRORS
216
+ APPSIGNAL_IGNORE_NAMESPACES APPSIGNAL_FILTER_PARAMETERS).each do |var|
214
217
  env_var = ENV[var]
215
218
  next unless env_var
216
219
  config[ENV_TO_KEY_MAPPING[var]] = env_var.split(",")
@@ -82,16 +82,11 @@ module Appsignal
82
82
  value
83
83
  end
84
84
  end
85
-
86
- def format_args(args)
87
- args.map do |arg|
88
- truncate(string_or_inspect(arg))
89
- end
90
- end
91
85
  end
92
86
  end
93
87
  end
94
88
 
89
+ require "appsignal/hooks/action_cable"
95
90
  require "appsignal/hooks/active_support_notifications"
96
91
  require "appsignal/hooks/celluloid"
97
92
  require "appsignal/hooks/delayed_job"
@@ -0,0 +1,113 @@
1
+ module Appsignal
2
+ class Hooks
3
+ # @api private
4
+ class ActionCableHook < Appsignal::Hooks::Hook
5
+ register :action_cable
6
+
7
+ REQUEST_ID = "_appsignal_action_cable.request_id".freeze
8
+
9
+ def dependencies_present?
10
+ defined?(::ActiveSupport::Notifications::Instrumenter) &&
11
+ defined?(::ActionCable)
12
+ end
13
+
14
+ def install
15
+ patch_perform_action
16
+ install_callbacks
17
+ end
18
+
19
+ private
20
+
21
+ def patch_perform_action
22
+ ActionCable::Channel::Base.class_eval do
23
+ alias_method :original_perform_action, :perform_action
24
+
25
+ def perform_action(*args, &block)
26
+ # The request is only the original websocket request
27
+ env = connection.env
28
+ request = ActionDispatch::Request.new(env)
29
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
30
+ request.request_id || SecureRandom.uuid
31
+
32
+ transaction = Appsignal::Transaction.create(
33
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
34
+ Appsignal::Transaction::ACTION_CABLE,
35
+ request
36
+ )
37
+
38
+ begin
39
+ original_perform_action(*args, &block)
40
+ rescue => exception
41
+ transaction.set_error(exception)
42
+ raise exception
43
+ ensure
44
+ transaction.params = args.first
45
+ transaction.set_action_if_nil("#{self.class}##{args.first["action"]}")
46
+ transaction.set_metadata("path", request.path)
47
+ transaction.set_metadata("method", "websocket")
48
+ Appsignal::Transaction.complete_current!
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def install_callbacks
55
+ ActionCable::Channel::Base.set_callback :subscribe, :around, :prepend => true do |channel, inner|
56
+ # The request is only the original websocket request
57
+ env = channel.connection.env
58
+ request = ActionDispatch::Request.new(env)
59
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
60
+ request.request_id || SecureRandom.uuid
61
+
62
+ transaction = Appsignal::Transaction.create(
63
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
64
+ Appsignal::Transaction::ACTION_CABLE,
65
+ request
66
+ )
67
+
68
+ begin
69
+ Appsignal.instrument "subscribed.action_cable" do
70
+ inner.call
71
+ end
72
+ rescue => exception
73
+ transaction.set_error(exception)
74
+ raise exception
75
+ ensure
76
+ transaction.set_action_if_nil("#{channel.class}#subscribed")
77
+ transaction.set_metadata("path", request.path)
78
+ transaction.set_metadata("method", "websocket")
79
+ Appsignal::Transaction.complete_current!
80
+ end
81
+ end
82
+
83
+ ActionCable::Channel::Base.set_callback :unsubscribe, :around, :prepend => true do |channel, inner|
84
+ # The request is only the original websocket request
85
+ env = channel.connection.env
86
+ request = ActionDispatch::Request.new(env)
87
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
88
+ request.request_id || SecureRandom.uuid
89
+
90
+ transaction = Appsignal::Transaction.create(
91
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
92
+ Appsignal::Transaction::ACTION_CABLE,
93
+ request
94
+ )
95
+
96
+ begin
97
+ Appsignal.instrument "unsubscribed.action_cable" do
98
+ inner.call
99
+ end
100
+ rescue => exception
101
+ transaction.set_error(exception)
102
+ raise exception
103
+ ensure
104
+ transaction.set_action_if_nil("#{channel.class}#unsubscribed")
105
+ transaction.set_metadata("path", request.path)
106
+ transaction.set_metadata("method", "websocket")
107
+ Appsignal::Transaction.complete_current!
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -11,6 +11,16 @@ module Appsignal
11
11
  end
12
12
 
13
13
  def install
14
+ ::ActiveSupport::Notifications.class_eval do
15
+ def self.instrument(name, payload = {})
16
+ # Don't check the notifier if any subscriber is listening:
17
+ # AppSignal is listening
18
+ instrumenter.instrument(name, payload) do
19
+ yield payload if block_given?
20
+ end
21
+ end
22
+ end
23
+
14
24
  ::ActiveSupport::Notifications::Instrumenter.class_eval do
15
25
  alias instrument_without_appsignal instrument
16
26
 
@@ -23,8 +33,8 @@ module Appsignal
23
33
  transaction.start_event
24
34
  end
25
35
 
26
- return_value = instrument_without_appsignal(name, payload, &block)
27
-
36
+ instrument_without_appsignal(name, payload, &block)
37
+ ensure
28
38
  if instrument_this
29
39
  title, body, body_format = Appsignal::EventFormatter.format(name, payload)
30
40
  transaction.finish_event(
@@ -34,8 +44,6 @@ module Appsignal
34
44
  body_format
35
45
  )
36
46
  end
37
-
38
- return_value
39
47
  end
40
48
  end
41
49
  end
@@ -2,21 +2,21 @@ module Appsignal
2
2
  class Hooks
3
3
  # @api private
4
4
  class ShoryukenMiddleware
5
- def call(_worker_instance, queue, sqs_msg, body)
6
- metadata = {
7
- :queue => queue
8
- }
9
- exclude_keys = [:job_class, :queue_name, :arguments]
10
- metadata.merge!(body.reject { |key| exclude_keys.member?(key.to_sym) })
11
- metadata.merge!(sqs_msg.attributes)
12
-
5
+ def call(worker_instance, queue, sqs_msg, body)
6
+ metadata = { :queue => queue }.merge(sqs_msg.attributes)
13
7
  options = {
14
- :class => body["job_class"],
8
+ :class => worker_instance.class.name,
15
9
  :method => "perform",
16
10
  :metadata => metadata
17
11
  }
18
- options[:params] = body["arguments"] if body.key?("arguments")
19
- options[:queue_start] = Time.at(sqs_msg.attributes["SentTimestamp"].to_i / 1000) if sqs_msg.attributes.key?("SentTimestamp")
12
+
13
+ args = body.is_a?(Hash) ? body : { :params => body }
14
+ options[:params] = Appsignal::Utils::ParamsSanitizer.sanitize args,
15
+ :filter_parameters => Appsignal.config[:filter_parameters]
16
+
17
+ if sqs_msg.attributes.key?("SentTimestamp")
18
+ options[:queue_start] = Time.at(sqs_msg.attributes["SentTimestamp"].to_i / 1000)
19
+ end
20
20
 
21
21
  Appsignal.monitor_transaction("perform_job.shoryuken", options) do
22
22
  yield
@@ -14,12 +14,14 @@ module Appsignal
14
14
  end
15
15
 
16
16
  def call(_worker, item, _queue)
17
- params =
17
+ args =
18
18
  if item["class"] == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
19
- format_args(item["args"].first["arguments"])
19
+ item["args"].first["arguments"]
20
20
  else
21
- format_args(item["args"])
21
+ item["args"]
22
22
  end
23
+ params = Appsignal::Utils::ParamsSanitizer.sanitize args,
24
+ :filter_parameters => Appsignal.config[:filter_parameters]
23
25
 
24
26
  Appsignal.monitor_transaction(
25
27
  "perform_job.sidekiq",
@@ -26,7 +26,11 @@ module Appsignal
26
26
  class_name, method_name = job.job_data[:name].split("#")
27
27
  args = job.job_data[:args] || {}
28
28
  job_data = job.job_data
29
+ else
30
+ args = {}
29
31
  end
32
+ params = Appsignal::Utils::ParamsSanitizer.sanitize args,
33
+ :filter_parameters => Appsignal.config[:filter_parameters]
30
34
 
31
35
  Appsignal.monitor_transaction(
32
36
  "perform_job.delayed_job",
@@ -38,7 +42,7 @@ module Appsignal
38
42
  :priority => extract_value(job_data, :priority, 0),
39
43
  :attempts => extract_value(job_data, :attempts, 0)
40
44
  },
41
- :params => format_args(args),
45
+ :params => params,
42
46
  :queue_start => extract_value(job_data, :created_at)
43
47
  ) do
44
48
  block.call(job)
@@ -7,11 +7,14 @@ module Appsignal
7
7
  def self.included(base)
8
8
  base.class_eval do
9
9
  around_perform do |job, block|
10
+ params = Appsignal::Utils::ParamsSanitizer.sanitize job.arguments,
11
+ :filter_parameters => Appsignal.config[:filter_parameters]
12
+
10
13
  Appsignal.monitor_single_transaction(
11
14
  "perform_job.resque",
12
15
  :class => job.class.to_s,
13
16
  :method => "perform",
14
- :params => job.format_args(job.arguments),
17
+ :params => params,
15
18
  :metadata => {
16
19
  :id => job.job_id,
17
20
  :queue => job.queue_name
@@ -4,6 +4,7 @@ module Appsignal
4
4
  class Transaction
5
5
  HTTP_REQUEST = "http_request".freeze
6
6
  BACKGROUND_JOB = "background_job".freeze
7
+ ACTION_CABLE = "action_cable".freeze
7
8
  FRONTEND = "frontend".freeze
8
9
  BLANK = "".freeze
9
10
 
@@ -32,7 +33,9 @@ module Appsignal
32
33
  # Check if we already have a running transaction
33
34
  if Thread.current[:appsignal_transaction] != nil
34
35
  # Log the issue and return the current transaction
35
- Appsignal.logger.debug("Trying to start new transaction #{id} but #{current.transaction_id} is already running. Using #{current.transaction_id}")
36
+ Appsignal.logger.debug "Trying to start new transaction with id " \
37
+ "'#{id}', but a transaction with id '#{current.transaction_id}' " \
38
+ "is already running. Using transaction '#{current.transaction_id}'."
36
39
 
37
40
  # Return the current (running) transaction
38
41
  current
@@ -61,6 +64,18 @@ module Appsignal
61
64
 
62
65
  attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded
63
66
 
67
+ # @!attribute params
68
+ # Attribute for parameters of the transaction.
69
+ #
70
+ # When no parameters are set with {#params=} the parameters it will look
71
+ # for parameters on the {#request} environment.
72
+ #
73
+ # The parameters set using {#params=} are leading over those extracted
74
+ # from a request's environment.
75
+ #
76
+ # @return [Hash]
77
+ attr_writer :params
78
+
64
79
  def initialize(transaction_id, namespace, request, options = {})
65
80
  @transaction_id = transaction_id
66
81
  @action = nil
@@ -86,7 +101,8 @@ module Appsignal
86
101
 
87
102
  def complete
88
103
  if discarded?
89
- Appsignal.logger.debug("Skipping transaction because it was manually discarded.".freeze)
104
+ Appsignal.logger.debug "Skipping transaction '#{transaction_id}' " \
105
+ "because it was manually discarded."
90
106
  return
91
107
  end
92
108
  if @ext.finish(self.class.garbage_collection_profiler.total_time)
@@ -123,6 +139,11 @@ module Appsignal
123
139
  @store[key]
124
140
  end
125
141
 
142
+ def params
143
+ return @params if defined?(@params)
144
+ request_params
145
+ end
146
+
126
147
  # Set tags on the transaction.
127
148
  #
128
149
  # @param given_tags [Hash] Collection of tags.
@@ -291,6 +312,11 @@ module Appsignal
291
312
  finish_event(name, title, body, body_format)
292
313
  end
293
314
 
315
+ def to_h
316
+ JSON.parse(@ext.to_json)
317
+ end
318
+ alias_method :to_hash, :to_h
319
+
294
320
  class GenericRequest
295
321
  attr_reader :env
296
322
 
@@ -348,17 +374,6 @@ module Appsignal
348
374
 
349
375
  def sanitized_params
350
376
  return unless Appsignal.config[:send_params]
351
- return unless request.respond_to?(options[:params_method])
352
-
353
- params =
354
- begin
355
- request.send options[:params_method]
356
- rescue => e
357
- # Getting params from the request has been know to fail.
358
- Appsignal.logger.debug "Exception while getting params: #{e}"
359
- nil
360
- end
361
- return unless params
362
377
 
363
378
  options = {}
364
379
  if Appsignal.config[:filter_parameters]
@@ -367,6 +382,18 @@ module Appsignal
367
382
  Appsignal::Utils::ParamsSanitizer.sanitize params, options
368
383
  end
369
384
 
385
+ def request_params
386
+ return unless request.respond_to?(options[:params_method])
387
+
388
+ begin
389
+ request.send options[:params_method]
390
+ rescue => e
391
+ # Getting params from the request has been know to fail.
392
+ Appsignal.logger.debug "Exception while getting params: #{e}"
393
+ nil
394
+ end
395
+ end
396
+
370
397
  # Returns sanitized environment for a transaction.
371
398
  #
372
399
  # The environment of a transaction can contain a lot of information, not