appsignal 3.10.0-java → 3.11.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +1 -0
  5. data/benchmark.rake +99 -42
  6. data/lib/appsignal/cli/demo.rb +0 -1
  7. data/lib/appsignal/config.rb +54 -98
  8. data/lib/appsignal/demo.rb +15 -20
  9. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  10. data/lib/appsignal/event_formatter.rb +3 -2
  11. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  12. data/lib/appsignal/hooks/action_cable.rb +21 -16
  13. data/lib/appsignal/hooks/active_job.rb +14 -8
  14. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  15. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  16. data/lib/appsignal/integrations/action_cable.rb +5 -7
  17. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  18. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  19. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  20. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  21. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  22. data/lib/appsignal/integrations/excon.rb +1 -0
  23. data/lib/appsignal/integrations/http.rb +1 -0
  24. data/lib/appsignal/integrations/net_http.rb +1 -0
  25. data/lib/appsignal/integrations/object.rb +6 -0
  26. data/lib/appsignal/integrations/que.rb +13 -20
  27. data/lib/appsignal/integrations/railtie.rb +1 -1
  28. data/lib/appsignal/integrations/rake.rb +1 -5
  29. data/lib/appsignal/integrations/redis.rb +1 -0
  30. data/lib/appsignal/integrations/redis_client.rb +1 -0
  31. data/lib/appsignal/integrations/resque.rb +2 -5
  32. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  33. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  34. data/lib/appsignal/integrations/unicorn.rb +1 -0
  35. data/lib/appsignal/integrations/webmachine.rb +2 -5
  36. data/lib/appsignal/logger.rb +7 -3
  37. data/lib/appsignal/probes/helpers.rb +1 -0
  38. data/lib/appsignal/probes/mri.rb +1 -0
  39. data/lib/appsignal/probes/sidekiq.rb +1 -0
  40. data/lib/appsignal/probes.rb +3 -0
  41. data/lib/appsignal/rack/abstract_middleware.rb +18 -12
  42. data/lib/appsignal/rack/event_handler.rb +39 -8
  43. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  44. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  45. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  46. data/lib/appsignal/rack.rb +29 -0
  47. data/lib/appsignal/span.rb +1 -0
  48. data/lib/appsignal/transaction.rb +308 -101
  49. data/lib/appsignal/utils/data.rb +0 -1
  50. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  51. data/lib/appsignal/utils/integration_logger.rb +0 -13
  52. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  53. data/lib/appsignal/utils/json.rb +0 -1
  54. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  55. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  56. data/lib/appsignal/utils.rb +6 -0
  57. data/lib/appsignal/version.rb +1 -1
  58. data/lib/appsignal.rb +6 -5
  59. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  60. data/spec/lib/appsignal/config_spec.rb +138 -43
  61. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  62. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  63. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  64. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  65. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  66. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  67. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  68. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  69. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  70. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
  71. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  72. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  73. data/spec/lib/appsignal/rack_spec.rb +63 -0
  74. data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
  75. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  76. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  77. data/spec/lib/appsignal_spec.rb +323 -10
  78. data/spec/support/helpers/transaction_helpers.rb +44 -20
  79. data/spec/support/matchers/transaction.rb +15 -1
  80. data/spec/support/testing.rb +1 -1
  81. metadata +6 -2
@@ -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
@@ -108,7 +105,7 @@ module Appsignal
108
105
  # {#instrument_app_call} this will report any exceptions being
109
106
  # raised.
110
107
  #
111
- # @see {#instrument_app_call}
108
+ # @see #instrument_app_call
112
109
  def instrument_app_call_with_exception_handling(env, transaction, wrapped_instrumentation)
113
110
  instrument_app_call(env, transaction)
114
111
  rescue Exception => error # rubocop:disable Lint/RescueException
@@ -147,7 +144,14 @@ module Appsignal
147
144
  transaction.set_metadata("method", request_method) if request_method
148
145
 
149
146
  transaction.set_params_if_nil { params_for(request) }
150
- transaction.set_http_or_background_queue_start
147
+ transaction.set_session_data_if_nil do
148
+ request.session if request.respond_to?(:session)
149
+ end
150
+ transaction.set_headers_if_nil do
151
+ request.env if request.respond_to?(:env)
152
+ end
153
+ queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
154
+ transaction.set_queue_start(queue_start) if queue_start
151
155
  end
152
156
 
153
157
  def params_for(request)
@@ -155,9 +159,9 @@ module Appsignal
155
159
 
156
160
  request.send(@params_method)
157
161
  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}"
162
+ Appsignal.internal_logger.error(
163
+ "Exception while fetching params from '#{@request_class}##{@params_method}': " \
164
+ "#{error.class} #{error}"
161
165
  )
162
166
  nil
163
167
  end
@@ -165,7 +169,9 @@ module Appsignal
165
169
  def request_method_for(request)
166
170
  request.request_method
167
171
  rescue => error
168
- Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
172
+ Appsignal.internal_logger.error(
173
+ "Exception while fetching the HTTP request method: #{error.class}: #{error}"
174
+ )
169
175
  nil
170
176
  end
171
177
 
@@ -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
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Appsignal
4
4
  module Rack
5
+ # @deprecated Use {InstrumentationMiddleware} instead.
5
6
  # @api private
6
7
  class GenericInstrumentation < AbstractMiddleware
7
8
  def initialize(app, options = {})
@@ -2,8 +2,9 @@
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
9
  options[:instrument_event_name] = "process_request.grape"
9
10
  options[:report_errors] = lambda { |env| !env["grape.skip_appsignal_error"] }
@@ -10,6 +10,7 @@ module Appsignal
10
10
  # Instrumentation middleware that tracks exceptions in streaming Rack
11
11
  # responses.
12
12
  #
13
+ # @deprecated Use {InstrumentationMiddleware} instead.
13
14
  # @api private
14
15
  class StreamingListener < AbstractMiddleware
15
16
  def initialize(app, options = {})
@@ -3,6 +3,35 @@
3
3
  module Appsignal
4
4
  # @api private
5
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
+
6
35
  # Alias constants that have moved with a warning message that points to the
7
36
  # place to update the reference.
8
37
  def self.const_missing(name)
@@ -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 || "")