appsignal 2.2.1 → 2.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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