appsignal 3.9.3-java → 3.11.0-java

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 (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
@@ -7,10 +7,32 @@ module Appsignal
7
7
  APPSIGNAL_EVENT_HANDLER_HAS_ERROR = "appsignal.event_handler.error"
8
8
  RACK_AFTER_REPLY = "rack.after_reply"
9
9
 
10
- # @api private
10
+ # Instrumentation middleware using Rack's Events module.
11
+ #
12
+ # We recommend using this in combination with the
13
+ # {InstrumentationMiddleware}.
14
+ #
15
+ # This middleware will report the response status code as the
16
+ # `response_status` tag on the sample. It will also report the response
17
+ # status as the `response_status` metric.
18
+ #
19
+ # This middleware will ensure the AppSignal transaction is always completed
20
+ # for every request.
21
+ #
22
+ # @example Add EventHandler to a Rack app
23
+ # # Add this middleware as the first middleware of an app
24
+ # use ::Rack::Events, [Appsignal::Rack::EventHandler.new]
25
+ #
26
+ # # Then add the InstrumentationMiddleware
27
+ # use Appsignal::Rack::InstrumentationMiddleware
28
+ #
29
+ # @see https://docs.appsignal.com/ruby/integrations/rack.html
30
+ # Rack integration documentation.
31
+ # @api public
11
32
  class EventHandler
12
33
  include ::Rack::Events::Abstract
13
34
 
35
+ # @api private
14
36
  def self.safe_execution(name)
15
37
  yield
16
38
  rescue => e
@@ -19,27 +41,27 @@ module Appsignal
19
41
  )
20
42
  end
21
43
 
44
+ # @api private
22
45
  attr_reader :id
23
46
 
47
+ # @api private
24
48
  def initialize
25
49
  @id = SecureRandom.uuid
26
50
  end
27
51
 
52
+ # @api private
28
53
  def request_handler?(given_id)
29
54
  id == given_id
30
55
  end
31
56
 
57
+ # @api private
32
58
  def on_start(request, _response)
33
59
  event_handler = self
34
60
  self.class.safe_execution("Appsignal::Rack::EventHandler#on_start") do
35
61
  request.env[APPSIGNAL_EVENT_HANDLER_ID] ||= id
36
62
  return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
37
63
 
38
- transaction = Appsignal::Transaction.create(
39
- SecureRandom.uuid,
40
- Appsignal::Transaction::HTTP_REQUEST,
41
- request
42
- )
64
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::HTTP_REQUEST)
43
65
  request.env[APPSIGNAL_TRANSACTION] = transaction
44
66
 
45
67
  request.env[RACK_AFTER_REPLY] ||= []
@@ -49,7 +71,8 @@ module Appsignal
49
71
  Appsignal::Rack::EventHandler
50
72
  .safe_execution("Appsignal::Rack::EventHandler's after_reply") do
51
73
  transaction.finish_event("process_request.rack", "", "")
52
- transaction.set_http_or_background_queue_start
74
+ queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
75
+ transaction.set_queue_start(queue_start) if queue_start
53
76
  end
54
77
 
55
78
  # Make sure the current transaction is always closed when the request
@@ -65,6 +88,7 @@ module Appsignal
65
88
  end
66
89
  end
67
90
 
91
+ # @api private
68
92
  def on_error(request, _response, error)
69
93
  self.class.safe_execution("Appsignal::Rack::EventHandler#on_error") do
70
94
  return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
@@ -77,6 +101,7 @@ module Appsignal
77
101
  end
78
102
  end
79
103
 
104
+ # @api private
80
105
  def on_finish(request, response)
81
106
  return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
82
107
 
@@ -85,7 +110,13 @@ module Appsignal
85
110
 
86
111
  self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
87
112
  transaction.finish_event("process_request.rack", "", "")
88
- transaction.set_http_or_background_queue_start
113
+ transaction.set_params_if_nil { request.params }
114
+ transaction.set_headers_if_nil { request.env }
115
+ transaction.set_session_data_if_nil do
116
+ request.session if request.respond_to?(:session)
117
+ end
118
+ queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
119
+ transaction.set_queue_start(queue_start) if queue_start
89
120
  response_status =
90
121
  if response
91
122
  response.status
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack"
4
-
5
3
  module Appsignal
6
- # @api private
7
4
  module Rack
5
+ # @deprecated Use {InstrumentationMiddleware} instead.
6
+ # @api private
8
7
  class GenericInstrumentation < AbstractMiddleware
9
8
  def initialize(app, options = {})
10
- options[:instrument_span_name] ||= "process_action.generic"
9
+ options[:instrument_event_name] ||= "process_action.generic"
11
10
  super
12
11
  end
13
12
 
@@ -16,5 +15,8 @@ module Appsignal
16
15
  transaction.set_action_if_nil("unknown")
17
16
  end
18
17
  end
18
+
19
+ # @api private
20
+ class GenericInstrumentationAlias < GenericInstrumentation; end
19
21
  end
20
22
  end
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Appsignal
4
4
  module Rack
5
- # @api private
5
+ # @api public
6
6
  class GrapeMiddleware < Appsignal::Rack::AbstractMiddleware
7
+ # @api private
7
8
  def initialize(app, options = {})
8
- options[:instrument_span_name] = "process_request.grape"
9
+ options[:instrument_event_name] = "process_request.grape"
9
10
  options[:report_errors] = lambda { |env| !env["grape.skip_appsignal_error"] }
10
11
  super
11
12
  end
@@ -6,7 +6,7 @@ module Appsignal
6
6
  class HanamiMiddleware < AbstractMiddleware
7
7
  def initialize(app, options = {})
8
8
  options[:params_method] ||= :params
9
- options[:instrument_span_name] ||= "process_action.hanami"
9
+ options[:instrument_event_name] ||= "process_action.hanami"
10
10
  super
11
11
  end
12
12
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Rack
5
+ # Rack instrumentation middleware.
6
+ #
7
+ # This Ruby gem automatically instruments several Rack based libraries,
8
+ # like Rails and Sinatra. This middleware does not need to be added
9
+ # manually to these frameworks.
10
+ #
11
+ # This instrumentation middleware will wrap an app and report how long the
12
+ # request and response took, report errors that occurred in the app, and
13
+ # report metadata about the request method and path.
14
+ #
15
+ # The action name for the endpoint is not set by default, which is required
16
+ # for performance monitoring. Set the action name in each endpoint using
17
+ # the {Appsignal::Helpers::Instrumentation#set_action} helper.
18
+ #
19
+ # If multiple of these middlewares, or
20
+ # {AbstractMiddleware} subclasses are present in an app, only the top
21
+ # middleware will report errors from apps and other middleware.
22
+ #
23
+ # This middleware is best used in combination with the {EventHandler}.
24
+ #
25
+ # @example
26
+ # # config.ru
27
+ # require "appsignal"
28
+ # # Configure and start AppSignal
29
+ #
30
+ # # Add the EventHandler first
31
+ # use ::Rack::Events, [Appsignal::Rack::EventHandler.new]
32
+ # # Add the instrumentation middleware second
33
+ # use Appsignal::Rack::InstrumentationMiddleware
34
+ #
35
+ # # Other middleware
36
+ #
37
+ # # Start app
38
+ #
39
+ # @example Customize instrumentation event category
40
+ # use Appsignal::Rack::InstrumentationMiddleware,
41
+ # :instrument_event_name => "custom.goup"
42
+ #
43
+ # @example Disable error reporting for this middleware
44
+ # use Appsignal::Rack::InstrumentationMiddleware, :report_errors => false
45
+ #
46
+ # @example Always report errors, even when wrapped by other instrumentation middleware
47
+ # use Appsignal::Rack::InstrumentationMiddleware, :report_errors => true
48
+ #
49
+ # @example Disable error reporting for this middleware based on the request env
50
+ # use Appsignal::Rack::InstrumentationMiddleware,
51
+ # :report_errors => lambda { |env| env["some_key"] == "some value" }
52
+ #
53
+ # @see https://docs.appsignal.com/ruby/integrations/rack.html
54
+ # @api public
55
+ class InstrumentationMiddleware < AbstractMiddleware
56
+ def initialize(app, options = {})
57
+ options[:instrument_event_name] ||= "process_request_middleware.rack"
58
+ super
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack"
4
-
5
3
  module Appsignal
6
4
  module Rack
7
5
  # @api private
@@ -9,7 +7,7 @@ module Appsignal
9
7
  def initialize(app, options = {})
10
8
  options[:request_class] ||= ActionDispatch::Request
11
9
  options[:params_method] ||= :filtered_parameters
12
- options[:instrument_span_name] = nil
10
+ options[:instrument_event_name] = nil
13
11
  options[:report_errors] = true
14
12
  super
15
13
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack"
4
-
5
3
  module Appsignal
6
4
  module Rack
7
5
  # Stub old middleware. Prevents Sinatra middleware being loaded twice.
@@ -34,7 +32,7 @@ module Appsignal
34
32
  def initialize(app, options = {})
35
33
  options[:request_class] ||= Sinatra::Request
36
34
  options[:params_method] ||= :params
37
- options[:instrument_span_name] ||= "process_action.sinatra"
35
+ options[:instrument_event_name] ||= "process_action.sinatra"
38
36
  super
39
37
  @raise_errors_on = raise_errors?(app)
40
38
  end
@@ -1,73 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
4
+ "The constant Appsignal::Rack::StreamingListener has been deprecated. " \
5
+ "Please update the constant name to " \
6
+ "Appsignal::Rack::InstrumentationMiddleware."
7
+
3
8
  module Appsignal
4
9
  module Rack
5
- # Appsignal module that tracks exceptions in Streaming rack responses.
10
+ # Instrumentation middleware that tracks exceptions in streaming Rack
11
+ # responses.
6
12
  #
13
+ # @deprecated Use {InstrumentationMiddleware} instead.
7
14
  # @api private
8
- class StreamingListener
15
+ class StreamingListener < AbstractMiddleware
9
16
  def initialize(app, options = {})
10
- Appsignal.internal_logger.debug "Initializing Appsignal::Rack::StreamingListener"
11
- @app = app
12
- @options = options
13
- end
14
-
15
- def call(env)
16
- if Appsignal.active?
17
- call_with_appsignal_monitoring(env)
18
- else
19
- @app.call(env)
20
- end
17
+ options[:instrument_event_name] ||= "process_streaming_request.rack"
18
+ super
21
19
  end
22
20
 
23
- def call_with_appsignal_monitoring(env)
24
- request = ::Rack::Request.new(env)
25
- transaction = Appsignal::Transaction.create(
26
- SecureRandom.uuid,
27
- Appsignal::Transaction::HTTP_REQUEST,
28
- request
29
- )
21
+ def add_transaction_metadata_after(transaction, request)
22
+ transaction.set_action_if_nil(request.env["appsignal.action"])
30
23
 
31
- # Instrument a `process_action`, to set params/action name
32
- status, headers, body =
33
- Appsignal.instrument("process_action.rack") do
34
- @app.call(env)
35
- rescue Exception => e # rubocop:disable Lint/RescueException
36
- transaction.set_error(e)
37
- raise e
38
- ensure
39
- transaction.set_action_if_nil(env["appsignal.action"])
40
- transaction.set_metadata("path", request.path)
41
- transaction.set_metadata("method", request.request_method)
42
- transaction.set_http_or_background_queue_start
43
- end
44
-
45
- # Wrap the result body with our StreamWrapper
46
- [status, headers, StreamWrapper.new(body, transaction)]
24
+ super
47
25
  end
48
26
  end
49
27
  end
50
-
51
- class StreamWrapper
52
- def initialize(stream, transaction)
53
- @stream = stream
54
- @transaction = transaction
55
- end
56
-
57
- def each(&block)
58
- @stream.each(&block)
59
- rescue Exception => e # rubocop:disable Lint/RescueException
60
- @transaction.set_error(e)
61
- raise e
62
- end
63
-
64
- def close
65
- @stream.close if @stream.respond_to?(:close)
66
- rescue Exception => e # rubocop:disable Lint/RescueException
67
- @transaction.set_error(e)
68
- raise e
69
- ensure
70
- Appsignal::Transaction.complete_current!
71
- end
72
- end
73
28
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module Rack
6
+ class Utils
7
+ # Fetch the queue start time from the request environment.
8
+ #
9
+ # @since 3.11.0
10
+ # @param env [Hash] Request environment hash.
11
+ # @return [Integer, NilClass]
12
+ def self.queue_start_from(env)
13
+ return unless env
14
+
15
+ env_var = env["HTTP_X_QUEUE_START"] || env["HTTP_X_REQUEST_START"]
16
+ return unless env_var
17
+
18
+ cleaned_value = env_var.tr("^0-9", "")
19
+ return if cleaned_value.empty?
20
+
21
+ value = cleaned_value.to_i
22
+ if value > 4_102_441_200_000
23
+ # Value is in microseconds. Transform to milliseconds.
24
+ value / 1_000
25
+ elsif value < 946_681_200_000
26
+ # Value is too low to be plausible
27
+ nil
28
+ else
29
+ # Value is in milliseconds
30
+ value
31
+ end
32
+ end
33
+ end
34
+
35
+ # Alias constants that have moved with a warning message that points to the
36
+ # place to update the reference.
37
+ def self.const_missing(name)
38
+ case name
39
+ when :GenericInstrumentation
40
+ require "appsignal/rack/generic_instrumentation"
41
+
42
+ callers = caller
43
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
44
+ "The constant Appsignal::Rack::GenericInstrumentation has been deprecated. " \
45
+ "Please use the new Appsignal::Rack::InstrumentationMiddleware middleware. " \
46
+ "This new middleware does not default the action name to 'unknown'. " \
47
+ "Set the action name for the endpoint using the Appsignal.set_action helper. " \
48
+ "Read our Rack docs for more information " \
49
+ "https://docs.appsignal.com/ruby/integrations/rack.html " \
50
+ "Update the constant name to " \
51
+ "Appsignal::Rack::InstrumentationMiddleware in the following file to " \
52
+ "remove this message.\n#{callers.first}"
53
+ # Return the alias so it can't ever get stuck in a recursive loop
54
+ Appsignal::Rack::GenericInstrumentationAlias
55
+ else
56
+ super
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
+ # @api private
4
5
  class Span
5
6
  def initialize(namespace = nil, ext = nil)
6
7
  @ext = ext || Appsignal::Extension::Span.root(namespace || "")