appsignal 3.10.0-java → 3.12.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +197 -0
  4. data/Gemfile +1 -0
  5. data/Rakefile +1 -1
  6. data/benchmark.rake +99 -42
  7. data/lib/appsignal/cli/demo.rb +0 -1
  8. data/lib/appsignal/cli/diagnose.rb +1 -1
  9. data/lib/appsignal/config.rb +204 -130
  10. data/lib/appsignal/demo.rb +16 -26
  11. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  12. data/lib/appsignal/event_formatter.rb +3 -2
  13. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  14. data/lib/appsignal/hooks/action_cable.rb +21 -16
  15. data/lib/appsignal/hooks/active_job.rb +14 -8
  16. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  17. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  18. data/lib/appsignal/integrations/action_cable.rb +5 -7
  19. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  20. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  21. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  22. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  23. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  24. data/lib/appsignal/integrations/excon.rb +1 -0
  25. data/lib/appsignal/integrations/grape.rb +7 -0
  26. data/lib/appsignal/integrations/hanami.rb +8 -43
  27. data/lib/appsignal/integrations/http.rb +1 -0
  28. data/lib/appsignal/integrations/net_http.rb +1 -0
  29. data/lib/appsignal/integrations/object.rb +6 -0
  30. data/lib/appsignal/integrations/padrino.rb +8 -73
  31. data/lib/appsignal/integrations/que.rb +13 -20
  32. data/lib/appsignal/integrations/railtie.rb +36 -14
  33. data/lib/appsignal/integrations/rake.rb +1 -5
  34. data/lib/appsignal/integrations/redis.rb +1 -0
  35. data/lib/appsignal/integrations/redis_client.rb +1 -0
  36. data/lib/appsignal/integrations/resque.rb +2 -5
  37. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  38. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  39. data/lib/appsignal/integrations/sinatra.rb +8 -19
  40. data/lib/appsignal/integrations/unicorn.rb +1 -0
  41. data/lib/appsignal/integrations/webmachine.rb +2 -5
  42. data/lib/appsignal/loaders/grape.rb +13 -0
  43. data/lib/appsignal/loaders/hanami.rb +40 -0
  44. data/lib/appsignal/loaders/padrino.rb +68 -0
  45. data/lib/appsignal/loaders/sinatra.rb +24 -0
  46. data/lib/appsignal/loaders.rb +92 -0
  47. data/lib/appsignal/logger.rb +7 -3
  48. data/lib/appsignal/probes/helpers.rb +1 -0
  49. data/lib/appsignal/probes/mri.rb +1 -0
  50. data/lib/appsignal/probes/sidekiq.rb +1 -0
  51. data/lib/appsignal/probes.rb +3 -0
  52. data/lib/appsignal/rack/abstract_middleware.rb +20 -13
  53. data/lib/appsignal/rack/event_handler.rb +44 -13
  54. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  55. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  56. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  57. data/lib/appsignal/rack.rb +35 -0
  58. data/lib/appsignal/span.rb +1 -0
  59. data/lib/appsignal/transaction.rb +308 -101
  60. data/lib/appsignal/utils/data.rb +0 -1
  61. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  62. data/lib/appsignal/utils/integration_logger.rb +0 -13
  63. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  64. data/lib/appsignal/utils/json.rb +0 -1
  65. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  66. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  67. data/lib/appsignal/utils.rb +6 -0
  68. data/lib/appsignal/version.rb +1 -1
  69. data/lib/appsignal.rb +169 -14
  70. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  71. data/spec/lib/appsignal/cli/demo_spec.rb +0 -1
  72. data/spec/lib/appsignal/cli/diagnose/paths_spec.rb +1 -1
  73. data/spec/lib/appsignal/cli/diagnose_spec.rb +0 -1
  74. data/spec/lib/appsignal/config_spec.rb +291 -44
  75. data/spec/lib/appsignal/demo_spec.rb +1 -2
  76. data/spec/lib/appsignal/environment_spec.rb +4 -2
  77. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  78. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +3 -6
  79. data/spec/lib/appsignal/hooks/activejob_spec.rb +12 -3
  80. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  81. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +4 -7
  82. data/spec/lib/appsignal/hooks/excon_spec.rb +3 -6
  83. data/spec/lib/appsignal/hooks/gvl_spec.rb +2 -2
  84. data/spec/lib/appsignal/hooks/http_spec.rb +1 -3
  85. data/spec/lib/appsignal/hooks/net_http_spec.rb +1 -1
  86. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -8
  87. data/spec/lib/appsignal/hooks/redis_spec.rb +3 -6
  88. data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
  89. data/spec/lib/appsignal/hooks/sequel_spec.rb +3 -5
  90. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  91. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +1 -1
  92. data/spec/lib/appsignal/hooks/webmachine_spec.rb +1 -1
  93. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  94. data/spec/lib/appsignal/integrations/grape_spec.rb +36 -0
  95. data/spec/lib/appsignal/integrations/hanami_spec.rb +9 -178
  96. data/spec/lib/appsignal/integrations/http_spec.rb +1 -5
  97. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +4 -2
  98. data/spec/lib/appsignal/integrations/net_http_spec.rb +1 -1
  99. data/spec/lib/appsignal/integrations/object_spec.rb +1 -3
  100. data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -330
  101. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  102. data/spec/lib/appsignal/integrations/railtie_spec.rb +275 -191
  103. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  104. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +15 -13
  105. data/spec/lib/appsignal/integrations/sinatra_spec.rb +9 -104
  106. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  107. data/spec/lib/appsignal/loaders/grape_spec.rb +12 -0
  108. data/spec/lib/appsignal/loaders/hanami_spec.rb +95 -0
  109. data/spec/lib/appsignal/loaders/padrino_spec.rb +277 -0
  110. data/spec/lib/appsignal/loaders/sinatra_spec.rb +47 -0
  111. data/spec/lib/appsignal/loaders_spec.rb +137 -0
  112. data/spec/lib/appsignal/probes/sidekiq_spec.rb +1 -1
  113. data/spec/lib/appsignal/probes_spec.rb +6 -5
  114. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +51 -5
  115. data/spec/lib/appsignal/rack/event_handler_spec.rb +114 -10
  116. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +1 -1
  117. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +2 -35
  118. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +1 -1
  119. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  120. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -3
  121. data/spec/lib/appsignal/rack_spec.rb +63 -0
  122. data/spec/lib/appsignal/span_spec.rb +1 -3
  123. data/spec/lib/appsignal/transaction_spec.rb +1640 -1075
  124. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  125. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  126. data/spec/lib/appsignal_spec.rb +601 -36
  127. data/spec/lib/puma/appsignal_spec.rb +0 -3
  128. data/spec/spec_helper.rb +5 -4
  129. data/spec/support/helpers/config_helpers.rb +2 -1
  130. data/spec/support/helpers/loader_helper.rb +21 -0
  131. data/spec/support/helpers/transaction_helpers.rb +44 -20
  132. data/spec/support/matchers/transaction.rb +15 -1
  133. data/spec/support/stubs/appsignal/loaders/loader_stub.rb +7 -0
  134. data/spec/support/testing.rb +47 -1
  135. metadata +19 -2
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ # @api private
6
+ class ShoryukenMiddleware
7
+ def call(worker_instance, queue, sqs_msg, body, &block)
8
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
9
+
10
+ Appsignal.instrument("perform_job.shoryuken", &block)
11
+ rescue Exception => error # rubocop:disable Lint/RescueException
12
+ transaction.set_error(error)
13
+ raise
14
+ ensure
15
+ batch = sqs_msg.is_a?(Array)
16
+ attributes = fetch_attributes(batch, sqs_msg)
17
+ transaction.set_action_if_nil("#{worker_instance.class.name}#perform")
18
+ transaction.set_params_if_nil { fetch_args(batch, sqs_msg, body) }
19
+ transaction.set_tags(attributes)
20
+ transaction.set_tags("queue" => queue)
21
+ transaction.set_tags("batch" => true) if batch
22
+
23
+ if attributes.key?("SentTimestamp")
24
+ transaction.set_queue_start(Time.at(attributes["SentTimestamp"].to_i).to_i)
25
+ end
26
+
27
+ Appsignal::Transaction.complete_current!
28
+ end
29
+
30
+ private
31
+
32
+ def fetch_attributes(batch, sqs_msg)
33
+ if batch
34
+ # We can't instrument batched message separately, the `yield` will
35
+ # perform all the batched messages.
36
+ # To provide somewhat useful metadata, Get first message based on
37
+ # SentTimestamp, and use its attributes as metadata for the
38
+ # transaction. We can't combine them all because then they would
39
+ # overwrite each other and the last message (in an sorted order)
40
+ # would be used as the source of the metadata. With the
41
+ # oldest/first message at least some useful information is stored
42
+ # such as the first received time and the number of retries for the
43
+ # first message. The newer message should have lower values and
44
+ # timestamps in their metadata.
45
+ first_msg =
46
+ sqs_msg.min do |a, b|
47
+ a.attributes["SentTimestamp"].to_i <=> b.attributes["SentTimestamp"].to_i
48
+ end
49
+ first_msg.attributes
50
+ else
51
+ sqs_msg.attributes.merge(:message_id => sqs_msg.message_id)
52
+ end
53
+ end
54
+
55
+ def fetch_args(batch, sqs_msg, body)
56
+ if batch
57
+ bodies = {}
58
+ sqs_msg.each_with_index do |msg, index|
59
+ # Store all separate bodies on a hash with the key being the
60
+ # message_id
61
+ bodies[msg.message_id] = body[index]
62
+ end
63
+ bodies
64
+ else
65
+ case body
66
+ when Hash
67
+ body
68
+ else
69
+ { :params => body }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -11,6 +11,7 @@ module Appsignal
11
11
  # about completing the transaction.
12
12
  #
13
13
  # Introduced in Sidekiq 5.1.
14
+ # @api private
14
15
  class SidekiqDeathHandler
15
16
  def call(_job_context, exception)
16
17
  return unless Appsignal.config[:sidekiq_report_errors] == "discard"
@@ -37,12 +38,7 @@ module Appsignal
37
38
  # Sidekiq error outside of the middleware scope.
38
39
  # Can be a job JSON parse error or some other error happening in
39
40
  # Sidekiq.
40
- transaction =
41
- Appsignal::Transaction.create(
42
- SecureRandom.uuid, # Newly generated job id
43
- Appsignal::Transaction::BACKGROUND_JOB,
44
- Appsignal::Transaction::GenericRequest.new({})
45
- )
41
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
46
42
  transaction.set_action_if_nil("SidekiqInternal")
47
43
  transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
48
44
  transaction.set_params_if_nil(:jobstr => sidekiq_context[:jobstr])
@@ -64,13 +60,7 @@ module Appsignal
64
60
 
65
61
  def call(_worker, item, _queue, &block)
66
62
  job_status = nil
67
- transaction = Appsignal::Transaction.create(
68
- item["jid"],
69
- Appsignal::Transaction::BACKGROUND_JOB,
70
- Appsignal::Transaction::GenericRequest.new(
71
- :queue_start => item["enqueued_at"]
72
- )
73
- )
63
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
74
64
  transaction.set_action_if_nil(formatted_action_name(item))
75
65
 
76
66
  formatted_metadata(item).each do |key, value|
@@ -83,8 +73,10 @@ module Appsignal
83
73
  raise exception
84
74
  ensure
85
75
  if transaction
86
- transaction.set_params_if_nil(parse_arguments(item))
87
- transaction.set_http_or_background_queue_start
76
+ transaction.set_params_if_nil { parse_arguments(item) }
77
+ queue_start = (item["enqueued_at"].to_f * 1000.0).to_i # Convert seconds to milliseconds
78
+ transaction.set_queue_start(queue_start)
79
+ transaction.set_tags(:request_id => item["jid"])
88
80
  Appsignal::Transaction.complete_current! unless exception
89
81
 
90
82
  queue = item["queue"] || "unknown"
@@ -1,24 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "appsignal"
4
- require "appsignal/rack/sinatra_instrumentation"
5
4
 
6
- Appsignal.internal_logger.debug("Loading Sinatra (#{Sinatra::VERSION}) integration")
5
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
6
+ "The 'require \"appsignal/integrations/sinatra\"' file require integration " \
7
+ "method is deprecated. " \
8
+ "Please follow the Sinatra setup guide in our docs for the new method: " \
9
+ "https://docs.appsignal.com/ruby/integrations/sinatra.html"
10
+ )
7
11
 
8
- unless Appsignal.active?
9
- app_settings = ::Sinatra::Application.settings
10
- Appsignal.config = Appsignal::Config.new(
11
- app_settings.root || Dir.pwd,
12
- app_settings.environment
13
- )
14
-
15
- Appsignal.start
16
- end
17
-
18
- if Appsignal.active?
19
- ::Sinatra::Base.use(
20
- ::Rack::Events,
21
- [Appsignal::Rack::EventHandler.new]
22
- )
23
- ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
24
- end
12
+ Appsignal.load(:sinatra)
13
+ Appsignal.start
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module UnicornIntegration
6
7
  # Make sure that appsignal is started and the last transaction
7
8
  # in a worker gets flushed.
@@ -10,11 +10,7 @@ module Appsignal
10
10
  if has_parent_transaction
11
11
  Appsignal::Transaction.current
12
12
  else
13
- Appsignal::Transaction.create(
14
- SecureRandom.uuid,
15
- Appsignal::Transaction::HTTP_REQUEST,
16
- request
17
- )
13
+ Appsignal::Transaction.create(Appsignal::Transaction::HTTP_REQUEST)
18
14
  end
19
15
 
20
16
  Appsignal.instrument("process_action.webmachine") do
@@ -23,6 +19,7 @@ module Appsignal
23
19
  ensure
24
20
  transaction.set_action_if_nil("#{resource.class.name}##{request.method}")
25
21
  transaction.set_params_if_nil(request.query)
22
+ transaction.set_headers_if_nil { request.headers if request.respond_to?(:headers) }
26
23
 
27
24
  Appsignal::Transaction.complete_current! unless has_parent_transaction
28
25
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class GrapeLoader < Loader
6
+ register :grape
7
+
8
+ def on_load
9
+ require "appsignal/rack/grape_middleware"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class HanamiLoader < Loader
6
+ register :hanami
7
+
8
+ def on_load
9
+ hanami_app_config = ::Hanami.app.config
10
+ register_config_defaults(
11
+ :root_path => hanami_app_config.root.to_s,
12
+ :env => hanami_app_config.env
13
+ )
14
+ end
15
+
16
+ def on_start
17
+ require "appsignal/rack/hanami_middleware"
18
+
19
+ hanami_app_config = ::Hanami.app.config
20
+ hanami_app_config.middleware.use(
21
+ ::Rack::Events,
22
+ [Appsignal::Rack::EventHandler.new]
23
+ )
24
+ hanami_app_config.middleware.use(Appsignal::Rack::HanamiMiddleware)
25
+
26
+ ::Hanami::Action.prepend Appsignal::Loaders::HanamiLoader::HanamiIntegration
27
+ end
28
+
29
+ module HanamiIntegration
30
+ def call(env)
31
+ super
32
+ ensure
33
+ transaction = env[::Appsignal::Rack::APPSIGNAL_TRANSACTION]
34
+
35
+ transaction&.set_action_if_nil(self.class.name)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class PadrinoLoader < Loader
6
+ register :padrino
7
+
8
+ def on_load
9
+ register_config_defaults(
10
+ :root_path => Padrino.mounted_root,
11
+ :env => Padrino.env
12
+ )
13
+ end
14
+
15
+ def on_start
16
+ require "appsignal/rack/sinatra_instrumentation"
17
+
18
+ Padrino::Application.prepend(Appsignal::Loaders::PadrinoLoader::PadrinoIntegration)
19
+
20
+ Padrino.before_load do
21
+ Padrino.use ::Rack::Events, [Appsignal::Rack::EventHandler.new]
22
+ Padrino.use Appsignal::Rack::SinatraBaseInstrumentation,
23
+ :instrument_event_name => "process_action.padrino"
24
+ end
25
+ end
26
+
27
+ module PadrinoIntegration
28
+ def route!(base = settings, pass_block = nil)
29
+ return super if !Appsignal.active? || env["sinatra.static_file"]
30
+
31
+ begin
32
+ super
33
+ ensure
34
+ transaction = Appsignal::Transaction.current
35
+ transaction.set_action_if_nil(get_payload_action(request))
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def get_payload_action(request)
42
+ # Short-circuit is there's no request object to obtain information from
43
+ return settings.name.to_s unless request
44
+
45
+ # Newer versions expose the action / controller on the request class.
46
+ # Newer versions also still expose a route_obj so we must prioritize the
47
+ # action/fullpath methods.
48
+ # The `request.action` and `request.controller` values are `nil` when a
49
+ # endpoint is not found, `""` if not specified by the user.
50
+ controller_name = request.controller if request.respond_to?(:controller)
51
+ action_name = request.action if request.respond_to?(:action)
52
+ action_name ||= ""
53
+
54
+ return "#{settings.name}:#{controller_name}##{action_name}" unless action_name.empty?
55
+
56
+ # Older versions of Padrino work with a route object
57
+ if request.respond_to?(:route_obj) && request.route_obj
58
+ return "#{settings.name}:#{request.route_obj.original_path}"
59
+ end
60
+
61
+ # Fall back to the application name if we haven't found an action name in
62
+ # any previous methods.
63
+ "#{settings.name}#unknown"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class SinatraLoader < Loader
6
+ register :sinatra
7
+
8
+ def on_load
9
+ app_settings = ::Sinatra::Application.settings
10
+ register_config_defaults(
11
+ :root_path => app_settings.root,
12
+ :env => app_settings.environment
13
+ )
14
+ end
15
+
16
+ def on_start
17
+ require "appsignal/rack/sinatra_instrumentation"
18
+
19
+ ::Sinatra::Base.use(::Rack::Events, [Appsignal::Rack::EventHandler.new])
20
+ ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module Loaders
6
+ class << self
7
+ def loaders
8
+ @loaders ||= {}
9
+ end
10
+
11
+ def instances
12
+ @instances ||= {}
13
+ end
14
+
15
+ def register(name, klass)
16
+ loaders[name.to_sym] = klass
17
+ end
18
+
19
+ def registered?(name)
20
+ loaders.key?(name)
21
+ end
22
+
23
+ def unregister(name)
24
+ loaders.delete(name)
25
+ end
26
+
27
+ def load(name_str)
28
+ name = name_str.to_sym
29
+
30
+ unless registered?(name)
31
+ require_loader(name)
32
+ unless registered?(name)
33
+ Appsignal.internal_logger
34
+ .warn("No loader found with the name '#{name}'.")
35
+ return
36
+ end
37
+ end
38
+
39
+ Appsignal.internal_logger.debug("Loading '#{name}' loader")
40
+
41
+ begin
42
+ loader_klass = loaders[name]
43
+ loader = loader_klass.new
44
+ instances[name] = loader
45
+ loader.on_load if loader.respond_to?(:on_load)
46
+ rescue => e
47
+ Appsignal.internal_logger.error(
48
+ "An error occurred while loading the '#{name}' loader: " \
49
+ "#{e.class}: #{e.message}\n#{e.backtrace}"
50
+ )
51
+ end
52
+ end
53
+
54
+ def start
55
+ instances.each do |name, instance|
56
+ Appsignal.internal_logger.debug("Starting '#{name}' loader")
57
+ begin
58
+ instance.on_start if instance.respond_to?(:on_start)
59
+ rescue => e
60
+ Appsignal.internal_logger.error(
61
+ "An error occurred while starting the '#{name}' loader: " \
62
+ "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
63
+ )
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def require_loader(name)
71
+ require "appsignal/loaders/#{name}"
72
+ rescue LoadError
73
+ nil
74
+ end
75
+ end
76
+
77
+ class Loader
78
+ class << self
79
+ attr_reader :loader_name
80
+
81
+ def register(name)
82
+ @loader_name = name
83
+ Loaders.register(name, self)
84
+ end
85
+ end
86
+
87
+ def register_config_defaults(options)
88
+ Appsignal::Config.add_loader_defaults(self.class.loader_name, options)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -4,7 +4,10 @@ require "logger"
4
4
  require "set"
5
5
 
6
6
  module Appsignal
7
- # Logger that flushes logs to the AppSignal logging service
7
+ # Logger that flushes logs to the AppSignal logging service.
8
+ #
9
+ # @see https://docs.appsignal.com/logging/platforms/integrations/ruby.html
10
+ # AppSignal Ruby logging documentation.
8
11
  class Logger < ::Logger
9
12
  PLAINTEXT = 0
10
13
  LOGFMT = 1
@@ -144,8 +147,9 @@ module Appsignal
144
147
  # as our logger directly inherits from Ruby base logger.
145
148
  #
146
149
  # Links:
147
- # https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger.rb#L60-L76
148
- # https://github.com/rails/rails/blob/main/activesupport/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/active_support/logger_silence.rb
150
+ #
151
+ # - https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger.rb#L60-L76
152
+ # - https://github.com/rails/rails/blob/main/activesupport/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/active_support/logger_silence.rb
149
153
  def silence(_severity = ERROR, &block)
150
154
  block.call
151
155
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
+ # @api private
5
6
  module Helpers
6
7
  private
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
+ # @api private
5
6
  class MriProbe
6
7
  include Helpers
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
+ # @api private
5
6
  class SidekiqProbe
6
7
  include Helpers
7
8
 
@@ -2,8 +2,10 @@
2
2
 
3
3
  module Appsignal
4
4
  module Probes
5
+ # @api private
5
6
  ITERATION_IN_SECONDS = 60
6
7
 
8
+ # @api private
7
9
  class ProbeCollection
8
10
  def initialize
9
11
  @probes = {}
@@ -72,6 +74,7 @@ module Appsignal
72
74
 
73
75
  # @see ProbeCollection
74
76
  # @return [ProbeCollection] Returns list of probes.
77
+ # @api private
75
78
  def probes
76
79
  @probes ||= ProbeCollection.new
77
80
  end
@@ -9,6 +9,7 @@ module Appsignal
9
9
  # Do not use this middleware directly. Instead use
10
10
  # {InstrumentationMiddleware}.
11
11
  #
12
+ # @abstract
12
13
  # @api private
13
14
  class AbstractMiddleware
14
15
  DEFAULT_ERROR_REPORTING = :default
@@ -33,11 +34,7 @@ module Appsignal
33
34
  if wrapped_instrumentation
34
35
  env[Appsignal::Rack::APPSIGNAL_TRANSACTION]
35
36
  else
36
- Appsignal::Transaction.create(
37
- SecureRandom.uuid,
38
- Appsignal::Transaction::HTTP_REQUEST,
39
- request
40
- )
37
+ Appsignal::Transaction.create(Appsignal::Transaction::HTTP_REQUEST)
41
38
  end
42
39
 
43
40
  unless wrapped_instrumentation
@@ -80,7 +77,7 @@ module Appsignal
80
77
  # Either another {AbstractMiddleware} or {EventHandler} is higher in the
81
78
  # stack and will report the exception and complete the transaction.
82
79
  #
83
- # @see {#instrument_app_call_with_exception_handling}
80
+ # @see #instrument_app_call_with_exception_handling
84
81
  def instrument_app_call(env, transaction)
85
82
  if @instrument_event_name
86
83
  Appsignal.instrument(@instrument_event_name) do
@@ -94,9 +91,10 @@ module Appsignal
94
91
  def call_app(env, transaction)
95
92
  status, headers, obody = @app.call(env)
96
93
  body =
97
- if obody.is_a? Appsignal::Rack::BodyWrapper
94
+ if env[Appsignal::Rack::APPSIGNAL_RESPONSE_INSTRUMENTED]
98
95
  obody
99
96
  else
97
+ env[Appsignal::Rack::APPSIGNAL_RESPONSE_INSTRUMENTED] = true
100
98
  # Instrument response body and closing of the response body
101
99
  Appsignal::Rack::BodyWrapper.wrap(obody, transaction)
102
100
  end
@@ -108,7 +106,7 @@ module Appsignal
108
106
  # {#instrument_app_call} this will report any exceptions being
109
107
  # raised.
110
108
  #
111
- # @see {#instrument_app_call}
109
+ # @see #instrument_app_call
112
110
  def instrument_app_call_with_exception_handling(env, transaction, wrapped_instrumentation)
113
111
  instrument_app_call(env, transaction)
114
112
  rescue Exception => error # rubocop:disable Lint/RescueException
@@ -147,7 +145,14 @@ module Appsignal
147
145
  transaction.set_metadata("method", request_method) if request_method
148
146
 
149
147
  transaction.set_params_if_nil { params_for(request) }
150
- transaction.set_http_or_background_queue_start
148
+ transaction.set_session_data_if_nil do
149
+ request.session if request.respond_to?(:session)
150
+ end
151
+ transaction.set_headers_if_nil do
152
+ request.env if request.respond_to?(:env)
153
+ end
154
+ queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
155
+ transaction.set_queue_start(queue_start) if queue_start
151
156
  end
152
157
 
153
158
  def params_for(request)
@@ -155,9 +160,9 @@ module Appsignal
155
160
 
156
161
  request.send(@params_method)
157
162
  rescue => error
158
- # Getting params from the request has been know to fail.
159
- Appsignal.internal_logger.debug(
160
- "Exception while getting params in #{self.class} from '#{@params_method}': #{error}"
163
+ Appsignal.internal_logger.error(
164
+ "Exception while fetching params from '#{@request_class}##{@params_method}': " \
165
+ "#{error.class} #{error}"
161
166
  )
162
167
  nil
163
168
  end
@@ -165,7 +170,9 @@ module Appsignal
165
170
  def request_method_for(request)
166
171
  request.request_method
167
172
  rescue => error
168
- Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
173
+ Appsignal.internal_logger.error(
174
+ "Exception while fetching the HTTP request method: #{error.class}: #{error}"
175
+ )
169
176
  nil
170
177
  end
171
178