appsignal 3.9.3 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +180 -0
  5. data/Gemfile +1 -0
  6. data/README.md +0 -1
  7. data/Rakefile +1 -1
  8. data/benchmark.rake +99 -42
  9. data/build_matrix.yml +10 -12
  10. data/gemfiles/webmachine1.gemfile +5 -4
  11. data/lib/appsignal/cli/demo.rb +0 -1
  12. data/lib/appsignal/config.rb +57 -97
  13. data/lib/appsignal/demo.rb +15 -20
  14. data/lib/appsignal/environment.rb +6 -1
  15. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  16. data/lib/appsignal/event_formatter.rb +3 -2
  17. data/lib/appsignal/helpers/instrumentation.rb +490 -16
  18. data/lib/appsignal/hooks/action_cable.rb +21 -16
  19. data/lib/appsignal/hooks/active_job.rb +15 -14
  20. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  21. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  22. data/lib/appsignal/integrations/action_cable.rb +5 -7
  23. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  25. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  27. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  28. data/lib/appsignal/integrations/excon.rb +1 -0
  29. data/lib/appsignal/integrations/http.rb +1 -0
  30. data/lib/appsignal/integrations/net_http.rb +1 -0
  31. data/lib/appsignal/integrations/object.rb +6 -0
  32. data/lib/appsignal/integrations/padrino.rb +21 -25
  33. data/lib/appsignal/integrations/que.rb +13 -20
  34. data/lib/appsignal/integrations/railtie.rb +1 -1
  35. data/lib/appsignal/integrations/rake.rb +45 -15
  36. data/lib/appsignal/integrations/redis.rb +1 -0
  37. data/lib/appsignal/integrations/redis_client.rb +1 -0
  38. data/lib/appsignal/integrations/resque.rb +2 -5
  39. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  40. data/lib/appsignal/integrations/sidekiq.rb +7 -25
  41. data/lib/appsignal/integrations/unicorn.rb +1 -0
  42. data/lib/appsignal/integrations/webmachine.rb +12 -9
  43. data/lib/appsignal/logger.rb +7 -3
  44. data/lib/appsignal/probes/helpers.rb +1 -0
  45. data/lib/appsignal/probes/mri.rb +1 -0
  46. data/lib/appsignal/probes/sidekiq.rb +1 -0
  47. data/lib/appsignal/probes.rb +3 -0
  48. data/lib/appsignal/rack/abstract_middleware.rb +67 -24
  49. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  50. data/lib/appsignal/rack/event_handler.rb +39 -8
  51. data/lib/appsignal/rack/generic_instrumentation.rb +6 -4
  52. data/lib/appsignal/rack/grape_middleware.rb +3 -2
  53. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  54. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  55. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  57. data/lib/appsignal/rack/streaming_listener.rb +14 -59
  58. data/lib/appsignal/rack.rb +60 -0
  59. data/lib/appsignal/span.rb +1 -0
  60. data/lib/appsignal/transaction.rb +353 -104
  61. data/lib/appsignal/utils/data.rb +0 -1
  62. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  63. data/lib/appsignal/utils/integration_logger.rb +0 -13
  64. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  65. data/lib/appsignal/utils/json.rb +0 -1
  66. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  67. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  68. data/lib/appsignal/utils.rb +6 -0
  69. data/lib/appsignal/version.rb +1 -1
  70. data/lib/appsignal.rb +9 -6
  71. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  72. data/spec/lib/appsignal/config_spec.rb +139 -43
  73. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  74. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  75. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  76. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  77. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  78. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  79. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  80. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  81. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  84. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -17
  85. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +144 -11
  86. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  87. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  88. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  89. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  90. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  91. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  92. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  93. data/spec/lib/appsignal/rack_spec.rb +63 -0
  94. data/spec/lib/appsignal/transaction_spec.rb +1675 -953
  95. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  96. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  97. data/spec/lib/appsignal_spec.rb +517 -13
  98. data/spec/support/helpers/transaction_helpers.rb +44 -20
  99. data/spec/support/matchers/transaction.rb +15 -1
  100. data/spec/support/mocks/dummy_app.rb +1 -1
  101. data/spec/support/testing.rb +1 -1
  102. metadata +12 -4
  103. data/support/check_versions +0 -22
@@ -54,25 +54,13 @@ module Appsignal
54
54
  # we do for Sidekiq.
55
55
  #
56
56
  # Prefer job_id from provider, instead of ActiveJob's internal ID.
57
- Appsignal::Transaction.create(
58
- job["provider_job_id"] || job["job_id"],
59
- Appsignal::Transaction::BACKGROUND_JOB,
60
- Appsignal::Transaction::GenericRequest.new({})
61
- )
57
+ Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
62
58
  end
63
59
 
64
60
  if transaction
65
- transaction.set_params_if_nil(
66
- Appsignal::Utils::HashSanitizer.sanitize(
67
- job["arguments"],
68
- Appsignal.config[:filter_parameters]
69
- )
70
- )
61
+ transaction.set_params_if_nil(job["arguments"])
71
62
 
72
63
  transaction_tags = ActiveJobHelpers.transaction_tags_for(job)
73
- transaction_tags["active_job_id"] = job["job_id"]
74
- provider_job_id = job["provider_job_id"]
75
- transaction_tags[:provider_job_id] = provider_job_id if provider_job_id
76
64
  transaction.set_tags(transaction_tags)
77
65
 
78
66
  transaction.set_action(ActiveJobHelpers.action_name(job))
@@ -159,12 +147,25 @@ module Appsignal
159
147
 
160
148
  def self.transaction_tags_for(job)
161
149
  tags = {}
150
+
162
151
  queue = job["queue_name"]
163
152
  tags[:queue] = queue if queue
153
+
164
154
  priority = job["priority"]
165
155
  tags[:priority] = priority if priority
156
+
166
157
  executions = job["executions"]
167
158
  tags[:executions] = executions.to_i + 1 if executions
159
+
160
+ job_id = job["job_id"]
161
+ tags[:active_job_id] = job_id
162
+
163
+ provider_job_id = job["provider_job_id"]
164
+ tags[:provider_job_id] = provider_job_id if provider_job_id
165
+
166
+ request_id = provider_job_id || job_id
167
+ tags[:request_id] = request_id if request_id
168
+
168
169
  tags
169
170
  end
170
171
 
@@ -14,7 +14,7 @@ module Appsignal
14
14
  # The DJ plugin is a subclass of Delayed::Plugin, so we can only
15
15
  # require this code if we're actually installing.
16
16
  require "appsignal/integrations/delayed_job_plugin"
17
- ::Delayed::Worker.plugins << Appsignal::Hooks::DelayedJobPlugin
17
+ ::Delayed::Worker.plugins << Appsignal::Integrations::DelayedJobPlugin
18
18
  end
19
19
  end
20
20
  end
@@ -3,68 +3,6 @@
3
3
  module Appsignal
4
4
  class Hooks
5
5
  # @api private
6
- class ShoryukenMiddleware
7
- def call(worker_instance, queue, sqs_msg, body, &block)
8
- batch = sqs_msg.is_a?(Array)
9
- attributes =
10
- if batch
11
- # We can't instrument batched message separately, the `yield` will
12
- # perform all the batched messages.
13
- # To provide somewhat useful metadata, Get first message based on
14
- # SentTimestamp, and use its attributes as metadata for the
15
- # transaction. We can't combine them all because then they would
16
- # overwrite each other and the last message (in an sorted order)
17
- # would be used as the source of the metadata. With the
18
- # oldest/first message at least some useful information is stored
19
- # such as the first received time and the number of retries for the
20
- # first message. The newer message should have lower values and
21
- # timestamps in their metadata.
22
- first_msg = sqs_msg.min do |a, b|
23
- a.attributes["SentTimestamp"].to_i <=> b.attributes["SentTimestamp"].to_i
24
- end
25
- # Add batch => true metadata so people can recognize when a
26
- # transaction is about a batch of messages.
27
- first_msg.attributes.merge(:batch => true)
28
- else
29
- sqs_msg.attributes.merge(:message_id => sqs_msg.message_id)
30
- end
31
- metadata = { :queue => queue }.merge(attributes)
32
- options = {
33
- :class => worker_instance.class.name,
34
- :method => "perform",
35
- :metadata => metadata
36
- }
37
-
38
- args =
39
- if batch
40
- bodies = {}
41
- sqs_msg.each_with_index do |msg, index|
42
- # Store all separate bodies on a hash with the key being the
43
- # message_id
44
- bodies[msg.message_id] = body[index]
45
- end
46
- bodies
47
- else
48
- case body
49
- when Hash
50
- body
51
- else
52
- { :params => body }
53
- end
54
- end
55
- options[:params] = Appsignal::Utils::HashSanitizer.sanitize(
56
- args,
57
- Appsignal.config[:filter_parameters]
58
- )
59
-
60
- if attributes.key?("SentTimestamp")
61
- options[:queue_start] = Time.at(attributes["SentTimestamp"].to_i / 1000)
62
- end
63
-
64
- Appsignal.monitor_transaction("perform_job.shoryuken", options, &block)
65
- end
66
- end
67
-
68
6
  class ShoryukenHook < Appsignal::Hooks::Hook
69
7
  register :shoryuken
70
8
 
@@ -73,9 +11,11 @@ module Appsignal
73
11
  end
74
12
 
75
13
  def install
14
+ require "appsignal/integrations/shoryuken"
15
+
76
16
  ::Shoryuken.configure_server do |config|
77
17
  config.server_middleware do |chain|
78
- chain.add Appsignal::Hooks::ShoryukenMiddleware
18
+ chain.add Appsignal::Integrations::ShoryukenMiddleware
79
19
  end
80
20
  end
81
21
  end
@@ -2,19 +2,16 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module ActionCableIntegration
6
7
  def perform_action(*args, &block)
7
8
  # The request is only the original websocket request
8
9
  env = connection.env
9
10
  request = ActionDispatch::Request.new(env)
10
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
11
- request.request_id || SecureRandom.uuid
11
+ request_id = request.request_id || SecureRandom.uuid
12
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
12
13
 
13
- transaction = Appsignal::Transaction.create(
14
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
15
- Appsignal::Transaction::ACTION_CABLE,
16
- request
17
- )
14
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
18
15
 
19
16
  begin
20
17
  super
@@ -26,6 +23,7 @@ module Appsignal
26
23
  transaction.set_action_if_nil("#{self.class}##{args.first["action"]}")
27
24
  transaction.set_metadata("path", request.path)
28
25
  transaction.set_metadata("method", "websocket")
26
+ transaction.set_tags(:request_id => request_id) if request_id
29
27
  Appsignal::Transaction.complete_current!
30
28
  end
31
29
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module ActiveSupportNotificationsIntegration
6
7
  BANG = "!"
7
8
 
@@ -1,47 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- # @todo Move to sub-namespace
5
- # @api private
6
- class Capistrano
7
- def self.tasks(config)
8
- config.load do # rubocop:disable Metrics/BlockLength
9
- after "deploy", "appsignal:deploy"
10
- after "deploy:migrations", "appsignal:deploy"
4
+ module Integrations
5
+ # @api private
6
+ class Capistrano
7
+ def self.tasks(config)
8
+ config.load do # rubocop:disable Metrics/BlockLength
9
+ after "deploy", "appsignal:deploy"
10
+ after "deploy:migrations", "appsignal:deploy"
11
11
 
12
- namespace :appsignal do
13
- task :deploy do
14
- env = fetch(:appsignal_env,
15
- fetch(:stage, fetch(:rails_env, fetch(:rack_env, "production"))))
16
- user = fetch(:appsignal_user, ENV["USER"] || ENV.fetch("USERNAME", nil))
17
- revision = fetch(:appsignal_revision, fetch(:current_revision))
12
+ namespace :appsignal do
13
+ task :deploy do
14
+ env = fetch(:appsignal_env,
15
+ fetch(:stage, fetch(:rails_env, fetch(:rack_env, "production"))))
16
+ user = fetch(:appsignal_user, ENV["USER"] || ENV.fetch("USERNAME", nil))
17
+ revision = fetch(:appsignal_revision, fetch(:current_revision))
18
18
 
19
- appsignal_config = Appsignal::Config.new(
20
- ENV.fetch("PWD", nil),
21
- env,
22
- {},
23
- Appsignal::Utils::IntegrationLogger.new(StringIO.new)
24
- ).tap do |c|
25
- fetch(:appsignal_config, {}).each do |key, value|
26
- c[key] = value
19
+ appsignal_config = Appsignal::Config.new(
20
+ ENV.fetch("PWD", nil),
21
+ env,
22
+ {},
23
+ Appsignal::Utils::IntegrationLogger.new(StringIO.new)
24
+ ).tap do |c|
25
+ fetch(:appsignal_config, {}).each do |key, value|
26
+ c[key] = value
27
+ end
28
+ c.validate
27
29
  end
28
- c.validate
29
- end
30
30
 
31
- if appsignal_config&.active?
32
- marker_data = {
33
- :revision => revision,
34
- :user => user
35
- }
31
+ if appsignal_config&.active?
32
+ marker_data = {
33
+ :revision => revision,
34
+ :user => user
35
+ }
36
36
 
37
- marker = Marker.new(marker_data, appsignal_config)
38
- if config.dry_run
39
- puts "Dry run: AppSignal deploy marker not actually sent."
37
+ marker = Marker.new(marker_data, appsignal_config)
38
+ if config.dry_run
39
+ puts "Dry run: AppSignal deploy marker not actually sent."
40
+ else
41
+ marker.transmit
42
+ end
40
43
  else
41
- marker.transmit
44
+ puts "Not notifying of deploy, config is not active for environment: #{env}"
42
45
  end
43
- else
44
- puts "Not notifying of deploy, config is not active for environment: #{env}"
45
46
  end
46
47
  end
47
48
  end
@@ -51,5 +52,5 @@ module Appsignal
51
52
  end
52
53
 
53
54
  if ::Capistrano::Configuration.instance
54
- Appsignal::Capistrano.tasks(::Capistrano::Configuration.instance)
55
+ Appsignal::Integrations::Capistrano.tasks(::Capistrano::Configuration.instance)
55
56
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  class Hooks
5
+ # @api private
5
6
  module DataMapperLogListener
6
7
  SQL_CLASSES = [
7
8
  "DataObjects::SqlServer::Connection",
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- class Hooks
4
+ module Integrations
5
5
  # @api private
6
6
  class DelayedJobPlugin < ::Delayed::Plugin
7
7
  extend Appsignal::Hooks::Helpers
@@ -17,52 +17,46 @@ module Appsignal
17
17
  end
18
18
 
19
19
  def self.invoke_with_instrumentation(job, block)
20
- payload = job.payload_object
20
+ transaction =
21
+ Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
21
22
 
23
+ Appsignal.instrument("perform_job.delayed_job") do
24
+ block.call(job)
25
+ end
26
+ rescue Exception => error # rubocop:disable Lint/RescueException
27
+ transaction.set_error(error)
28
+ raise
29
+ ensure
30
+ payload = job.payload_object
22
31
  if payload.respond_to? :job_data
23
32
  # ActiveJob
24
33
  job_data = payload.job_data
25
- args = job_data.fetch("arguments", {})
26
- class_name = job_data["job_class"]
27
- method_name = "perform"
34
+ transaction.set_action_if_nil("#{job_data["job_class"]}#perform")
35
+ transaction.set_params_if_nil(job_data.fetch("arguments", {}))
28
36
  else
29
37
  # Delayed Job
30
- args = extract_value(payload, :args, {})
31
- class_name, method_name = class_and_method_name_from_object_or_hash(payload, job.name)
38
+ transaction.set_action_if_nil(action_name_from_payload(payload, job.name))
39
+ transaction.set_params_if_nil(extract_value(payload, :args, {}))
32
40
  end
33
41
 
34
- params = Appsignal::Utils::HashSanitizer.sanitize(
35
- args,
36
- Appsignal.config[:filter_parameters]
42
+ transaction.set_tags(
43
+ :id => extract_value(job, :id, nil, true),
44
+ :queue => extract_value(job, :queue),
45
+ :priority => extract_value(job, :priority, 0),
46
+ :attempts => extract_value(job, :attempts, 0)
37
47
  )
38
48
 
39
- Appsignal.monitor_transaction(
40
- "perform_job.delayed_job",
41
- :class => class_name,
42
- :method => method_name,
43
- :metadata => {
44
- :id => extract_value(job, :id, nil, true),
45
- :queue => extract_value(job, :queue),
46
- :priority => extract_value(job, :priority, 0),
47
- :attempts => extract_value(job, :attempts, 0)
48
- },
49
- :params => params,
50
- :queue_start => extract_value(job, :run_at)
51
- ) do
52
- block.call(job)
53
- end
49
+ transaction.set_queue_start(extract_value(job, :run_at)&.to_i&.* 1_000)
50
+
51
+ Appsignal::Transaction.complete_current!
54
52
  end
55
53
 
56
- def self.class_and_method_name_from_object_or_hash(payload, default_name)
54
+ def self.action_name_from_payload(payload, default_name)
57
55
  # Attempt to find appsignal_name override
58
56
  class_and_method_name = extract_value(payload, :appsignal_name, nil)
59
- return class_and_method_name.split("#") if class_and_method_name.is_a?(String)
60
-
61
- pound_split = default_name.split("#")
62
- return pound_split if pound_split.length == 2
63
-
64
- dot_split = default_name.split(".")
65
- return default_name if dot_split.length == 2
57
+ return class_and_method_name if class_and_method_name.is_a?(String)
58
+ return default_name if default_name.split("#").length == 2
59
+ return default_name if default_name.split(".").length == 2
66
60
 
67
61
  "#{default_name}#perform"
68
62
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module DryMonitorIntegration
6
7
  def instrument(event_id, payload = {}, &block)
7
8
  Appsignal::Transaction.current.start_event
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module ExconIntegration
6
7
  def self.instrument(name, data, &block)
7
8
  namespace, *event = name.split(".")
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module HttpIntegration
6
7
  def request(verb, uri, opts = {})
7
8
  parsed_request_uri = uri.is_a?(URI) ? uri : URI.parse(uri.to_s)
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module NetHttpIntegration
6
7
  def request(request, body = nil, &block)
7
8
  Appsignal.instrument(
@@ -3,6 +3,8 @@
3
3
  Appsignal::Environment.report_enabled("object_instrumentation") if defined?(Appsignal)
4
4
 
5
5
  class Object
6
+ # @see https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html
7
+ # Method instrumentation documentation.
6
8
  def self.appsignal_instrument_class_method(method_name, options = {})
7
9
  singleton_class.send \
8
10
  :alias_method, "appsignal_uninstrumented_#{method_name}", method_name
@@ -20,6 +22,8 @@ class Object
20
22
  end
21
23
  end
22
24
 
25
+ # @see https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html
26
+ # Method instrumentation documentation.
23
27
  def self.appsignal_instrument_method(method_name, options = {})
24
28
  alias_method "appsignal_uninstrumented_#{method_name}", method_name
25
29
  define_method method_name do |*args, &block|
@@ -33,12 +37,14 @@ class Object
33
37
  ruby2_keywords method_name if respond_to?(:ruby2_keywords, true)
34
38
  end
35
39
 
40
+ # @api private
36
41
  def self.appsignal_reverse_class_name
37
42
  return "AnonymousClass" unless name
38
43
 
39
44
  name.split("::").reverse.join(".")
40
45
  end
41
46
 
47
+ # @api private
42
48
  def appsignal_reverse_class_name
43
49
  self.class.appsignal_reverse_class_name
44
50
  end
@@ -1,18 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "appsignal"
4
+ require "appsignal/rack/sinatra_instrumentation"
4
5
 
5
6
  module Appsignal
6
7
  module Integrations
7
8
  # @api private
8
9
  module PadrinoPlugin
9
10
  def self.init
10
- Appsignal.internal_logger.debug("Loading Padrino (#{Padrino::VERSION}) integration")
11
+ Padrino::Application.prepend Appsignal::Integrations::PadrinoIntegration
11
12
 
12
- root = Padrino.mounted_root
13
- Appsignal.config = Appsignal::Config.new(root, Padrino.env)
13
+ Padrino.before_load do
14
+ Appsignal.internal_logger.debug("Loading Padrino (#{Padrino::VERSION}) integration")
14
15
 
15
- Appsignal.start
16
+ unless Appsignal.active?
17
+ root = Padrino.mounted_root
18
+ Appsignal.config = Appsignal::Config.new(root, Padrino.env)
19
+ Appsignal.start
20
+ end
21
+
22
+ next unless Appsignal.active?
23
+
24
+ Padrino.use ::Rack::Events, [Appsignal::Rack::EventHandler.new]
25
+ Padrino.use Appsignal::Rack::SinatraBaseInstrumentation,
26
+ :instrument_event_name => "process_action.padrino"
27
+ end
16
28
  end
17
29
  end
18
30
  end
@@ -20,35 +32,23 @@ end
20
32
 
21
33
  module Appsignal
22
34
  module Integrations
35
+ # @api private
23
36
  module PadrinoIntegration
24
37
  def route!(base = settings, pass_block = nil)
25
38
  return super if !Appsignal.active? || env["sinatra.static_file"]
26
39
 
27
- transaction = Appsignal::Transaction.create(
28
- SecureRandom.uuid,
29
- Appsignal::Transaction::HTTP_REQUEST,
30
- request
31
- )
32
40
  begin
33
- Appsignal.instrument("process_action.padrino") do
34
- super
35
- end
36
- rescue Exception => error # rubocop:disable Lint/RescueException
37
- transaction.set_error(error)
38
- raise error
41
+ super
39
42
  ensure
43
+ transaction = Appsignal::Transaction.current
40
44
  transaction.set_action_if_nil(get_payload_action(request))
41
- transaction.set_metadata("path", request.path)
42
- transaction.set_metadata("method", request.request_method)
43
- transaction.set_http_or_background_queue_start
44
- Appsignal::Transaction.complete_current!
45
45
  end
46
46
  end
47
47
 
48
48
  private
49
49
 
50
50
  def get_payload_action(request)
51
- # Short-circut is there's no request object to obtain information from
51
+ # Short-circuit is there's no request object to obtain information from
52
52
  return settings.name.to_s unless request
53
53
 
54
54
  # Newer versions expose the action / controller on the request class.
@@ -75,8 +75,4 @@ module Appsignal
75
75
  end
76
76
  end
77
77
 
78
- Padrino::Application.prepend Appsignal::Integrations::PadrinoIntegration
79
-
80
- Padrino.after_load do
81
- Appsignal::Integrations::PadrinoPlugin.init
82
- end
78
+ Appsignal::Integrations::PadrinoPlugin.init
@@ -2,27 +2,11 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module QuePlugin
6
7
  def _run(*)
7
- local_attrs = respond_to?(:que_attrs) ? que_attrs : attrs
8
- env = {
9
- :metadata => {
10
- :id => local_attrs[:job_id] || local_attrs[:id],
11
- :queue => local_attrs[:queue],
12
- :run_at => local_attrs[:run_at].to_s,
13
- :priority => local_attrs[:priority],
14
- :attempts => local_attrs[:error_count].to_i
15
- },
16
- :params => local_attrs[:args]
17
- }
18
-
19
- request = Appsignal::Transaction::GenericRequest.new(env)
20
-
21
- transaction = Appsignal::Transaction.create(
22
- SecureRandom.uuid,
23
- Appsignal::Transaction::BACKGROUND_JOB,
24
- request
25
- )
8
+ transaction =
9
+ Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
26
10
 
27
11
  begin
28
12
  Appsignal.instrument("perform_job.que") { super }
@@ -30,7 +14,16 @@ module Appsignal
30
14
  transaction.set_error(error)
31
15
  raise error
32
16
  ensure
33
- transaction.set_action_if_nil "#{local_attrs[:job_class]}#run"
17
+ local_attrs = respond_to?(:que_attrs) ? que_attrs : attrs
18
+ transaction.set_action_if_nil("#{local_attrs[:job_class]}#run")
19
+ transaction.set_params_if_nil(local_attrs[:args])
20
+ transaction.set_tags(
21
+ "id" => local_attrs[:job_id] || local_attrs[:id],
22
+ "queue" => local_attrs[:queue],
23
+ "run_at" => local_attrs[:run_at].to_s,
24
+ "priority" => local_attrs[:priority],
25
+ "attempts" => local_attrs[:error_count].to_i
26
+ )
34
27
  Appsignal::Transaction.complete_current!
35
28
  end
36
29
  end
@@ -69,7 +69,7 @@ module Appsignal
69
69
  transaction.set_metadata("path", path)
70
70
  transaction.set_metadata("method", method)
71
71
  transaction.set_params_if_nil(params)
72
- transaction.set_sample_data("custom_data", custom_data) if custom_data
72
+ transaction.set_custom_data(custom_data) if custom_data
73
73
 
74
74
  tags[:severity] = severity
75
75
  tags[:source] = source.to_s if source
@@ -2,26 +2,56 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module RakeIntegration
6
7
  def execute(*args)
7
- super
8
+ transaction =
9
+ if Appsignal.config[:enable_rake_performance_instrumentation]
10
+ Appsignal::Integrations::RakeIntegrationHelper.register_at_exit_hook
11
+ _appsignal_create_transaction
12
+ end
13
+
14
+ Appsignal.instrument "task.rake" do
15
+ super
16
+ end
8
17
  rescue Exception => error # rubocop:disable Lint/RescueException
9
- # Format given arguments and cast to hash if possible
10
- params, _ = args
11
- params = params.to_hash if params.respond_to?(:to_hash)
12
-
13
- transaction = Appsignal::Transaction.create(
14
- SecureRandom.uuid,
15
- Appsignal::Transaction::BACKGROUND_JOB,
16
- Appsignal::Transaction::GenericRequest.new(
17
- :params => params
18
- )
19
- )
20
- transaction.set_action(name)
18
+ Appsignal::Integrations::RakeIntegrationHelper.register_at_exit_hook
19
+ transaction ||= _appsignal_create_transaction
21
20
  transaction.set_error(error)
22
- transaction.complete
23
- Appsignal.stop("rake")
24
21
  raise error
22
+ ensure
23
+ if transaction
24
+ # Format given arguments and cast to hash if possible
25
+ params, _ = args
26
+ params = params.to_hash if params.respond_to?(:to_hash)
27
+ transaction.set_params_if_nil(params)
28
+ transaction.set_action(name)
29
+ transaction.complete
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def _appsignal_create_transaction
36
+ Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
37
+ end
38
+ end
39
+
40
+ # @api private
41
+ module RakeIntegrationHelper
42
+ # Register an `at_exit` hook when a task is executed. This will stop
43
+ # AppSignal when _all_ tasks are executed and Rake exits.
44
+ def self.register_at_exit_hook
45
+ return if @register_at_exit_hook
46
+
47
+ Kernel.at_exit(&method(:at_exit_hook))
48
+
49
+ @register_at_exit_hook = true
50
+ end
51
+
52
+ # The at_exit hook itself
53
+ def self.at_exit_hook
54
+ Appsignal.stop("rake")
25
55
  end
26
56
  end
27
57
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module RedisIntegration
6
7
  def write(command)
7
8
  sanitized_command =