appsignal 3.9.1-java → 3.9.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3135 -0
  3. data/.rubocop.yml +28 -20
  4. data/.rubocop_todo.yml +7 -33
  5. data/CHANGELOG.md +58 -0
  6. data/Rakefile +79 -64
  7. data/appsignal.gemspec +1 -1
  8. data/build_matrix.yml +109 -179
  9. data/ext/base.rb +1 -1
  10. data/gemfiles/hanami-2.1.gemfile +7 -0
  11. data/lib/appsignal/cli/diagnose.rb +1 -1
  12. data/lib/appsignal/config.rb +1 -1
  13. data/lib/appsignal/demo.rb +0 -1
  14. data/lib/appsignal/environment.rb +5 -1
  15. data/lib/appsignal/extension/jruby.rb +1 -1
  16. data/lib/appsignal/helpers/instrumentation.rb +3 -3
  17. data/lib/appsignal/hooks/active_job.rb +2 -1
  18. data/lib/appsignal/integrations/action_cable.rb +1 -1
  19. data/lib/appsignal/integrations/grape.rb +19 -47
  20. data/lib/appsignal/integrations/hanami.rb +27 -41
  21. data/lib/appsignal/integrations/padrino.rb +46 -43
  22. data/lib/appsignal/integrations/railtie.rb +1 -4
  23. data/lib/appsignal/integrations/resque.rb +1 -1
  24. data/lib/appsignal/integrations/sidekiq.rb +2 -4
  25. data/lib/appsignal/integrations/sinatra.rb +7 -2
  26. data/lib/appsignal/probes/gvl.rb +24 -2
  27. data/lib/appsignal/probes/sidekiq.rb +1 -1
  28. data/lib/appsignal/probes.rb +1 -1
  29. data/lib/appsignal/rack/abstract_middleware.rb +62 -28
  30. data/lib/appsignal/rack/event_handler.rb +37 -26
  31. data/lib/appsignal/rack/grape_middleware.rb +40 -0
  32. data/lib/appsignal/rack/hanami_middleware.rb +20 -0
  33. data/lib/appsignal/rack/rails_instrumentation.rb +14 -56
  34. data/lib/appsignal/utils/integration_memory_logger.rb +78 -0
  35. data/lib/appsignal/utils.rb +1 -0
  36. data/lib/appsignal/version.rb +1 -1
  37. data/lib/appsignal.rb +34 -33
  38. data/spec/.rubocop.yml +1 -1
  39. data/spec/lib/appsignal/cli/diagnose_spec.rb +1 -1
  40. data/spec/lib/appsignal/cli/install_spec.rb +3 -3
  41. data/spec/lib/appsignal/config_spec.rb +7 -5
  42. data/spec/lib/appsignal/demo_spec.rb +38 -41
  43. data/spec/lib/appsignal/hooks/action_cable_spec.rb +86 -167
  44. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +8 -20
  45. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +38 -84
  46. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +16 -37
  47. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +4 -4
  48. data/spec/lib/appsignal/hooks/activejob_spec.rb +111 -200
  49. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +54 -91
  50. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +14 -32
  51. data/spec/lib/appsignal/hooks/excon_spec.rb +8 -12
  52. data/spec/lib/appsignal/hooks/net_http_spec.rb +7 -42
  53. data/spec/lib/appsignal/hooks/rake_spec.rb +9 -19
  54. data/spec/lib/appsignal/hooks/redis_client_spec.rb +18 -30
  55. data/spec/lib/appsignal/hooks/redis_spec.rb +10 -16
  56. data/spec/lib/appsignal/hooks/resque_spec.rb +42 -62
  57. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +33 -74
  58. data/spec/lib/appsignal/integrations/hanami_spec.rb +126 -64
  59. data/spec/lib/appsignal/integrations/http_spec.rb +12 -20
  60. data/spec/lib/appsignal/integrations/net_http_spec.rb +33 -0
  61. data/spec/lib/appsignal/integrations/object_spec.rb +29 -36
  62. data/spec/lib/appsignal/integrations/padrino_spec.rb +47 -70
  63. data/spec/lib/appsignal/integrations/que_spec.rb +43 -70
  64. data/spec/lib/appsignal/integrations/railtie_spec.rb +26 -67
  65. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +86 -160
  66. data/spec/lib/appsignal/integrations/sinatra_spec.rb +8 -3
  67. data/spec/lib/appsignal/integrations/webmachine_spec.rb +28 -39
  68. data/spec/lib/appsignal/probes/gvl_spec.rb +80 -3
  69. data/spec/lib/appsignal/probes_spec.rb +7 -4
  70. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +215 -106
  71. data/spec/lib/appsignal/rack/event_handler_spec.rb +151 -69
  72. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +2 -12
  73. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +234 -0
  74. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +36 -0
  75. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +67 -131
  76. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +36 -44
  77. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +68 -86
  78. data/spec/lib/appsignal/transaction_spec.rb +79 -93
  79. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +163 -0
  80. data/spec/lib/appsignal_spec.rb +363 -342
  81. data/spec/support/hanami/hanami_app.rb +1 -3
  82. data/spec/support/helpers/dependency_helper.rb +6 -1
  83. data/spec/support/helpers/std_streams_helper.rb +1 -1
  84. data/spec/support/helpers/transaction_helpers.rb +8 -0
  85. data/spec/support/matchers/transaction.rb +185 -0
  86. data/spec/support/mocks/dummy_app.rb +20 -0
  87. data/spec/support/shared_examples/instrument.rb +17 -12
  88. data/spec/support/testing.rb +18 -9
  89. metadata +17 -10
  90. data/.semaphore/semaphore.yml +0 -2347
  91. data/script/lint_git +0 -22
  92. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -239
  93. data/spec/support/matchers/be_completed.rb +0 -5
  94. /data/gemfiles/{hanami.gemfile → hanami-2.0.gemfile} +0 -0
@@ -12,63 +12,66 @@ module Appsignal
12
12
  root = Padrino.mounted_root
13
13
  Appsignal.config = Appsignal::Config.new(root, Padrino.env)
14
14
 
15
- Appsignal.start_logger
16
15
  Appsignal.start
17
16
  end
18
17
  end
19
18
  end
20
19
  end
21
20
 
22
- module Appsignal::Integrations::PadrinoIntegration
23
- def route!(base = settings, pass_block = nil)
24
- return super if !Appsignal.active? || env["sinatra.static_file"]
21
+ module Appsignal
22
+ module Integrations
23
+ module PadrinoIntegration
24
+ def route!(base = settings, pass_block = nil)
25
+ return super if !Appsignal.active? || env["sinatra.static_file"]
25
26
 
26
- transaction = Appsignal::Transaction.create(
27
- SecureRandom.uuid,
28
- Appsignal::Transaction::HTTP_REQUEST,
29
- request
30
- )
31
- begin
32
- Appsignal.instrument("process_action.padrino") do
33
- super
27
+ transaction = Appsignal::Transaction.create(
28
+ SecureRandom.uuid,
29
+ Appsignal::Transaction::HTTP_REQUEST,
30
+ request
31
+ )
32
+ 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
39
+ ensure
40
+ 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
+ end
34
46
  end
35
- rescue Exception => error # rubocop:disable Lint/RescueException
36
- transaction.set_error(error)
37
- raise error
38
- ensure
39
- transaction.set_action_if_nil(get_payload_action(request))
40
- transaction.set_metadata("path", request.path)
41
- transaction.set_metadata("method", request.request_method)
42
- transaction.set_http_or_background_queue_start
43
- Appsignal::Transaction.complete_current!
44
- end
45
- end
46
47
 
47
- private
48
+ private
48
49
 
49
- def get_payload_action(request)
50
- # Short-circut is there's no request object to obtain information from
51
- return settings.name.to_s unless request
50
+ def get_payload_action(request)
51
+ # Short-circut is there's no request object to obtain information from
52
+ return settings.name.to_s unless request
52
53
 
53
- # Newer versions expose the action / controller on the request class.
54
- # Newer versions also still expose a route_obj so we must prioritize the
55
- # action/fullpath methods.
56
- # The `request.action` and `request.controller` values are `nil` when a
57
- # endpoint is not found, `""` if not specified by the user.
58
- controller_name = request.controller if request.respond_to?(:controller)
59
- action_name = request.action if request.respond_to?(:action)
60
- action_name ||= ""
54
+ # Newer versions expose the action / controller on the request class.
55
+ # Newer versions also still expose a route_obj so we must prioritize the
56
+ # action/fullpath methods.
57
+ # The `request.action` and `request.controller` values are `nil` when a
58
+ # endpoint is not found, `""` if not specified by the user.
59
+ controller_name = request.controller if request.respond_to?(:controller)
60
+ action_name = request.action if request.respond_to?(:action)
61
+ action_name ||= ""
61
62
 
62
- return "#{settings.name}:#{controller_name}##{action_name}" unless action_name.empty?
63
+ return "#{settings.name}:#{controller_name}##{action_name}" unless action_name.empty?
63
64
 
64
- # Older versions of Padrino work with a route object
65
- if request.respond_to?(:route_obj) && request.route_obj
66
- return "#{settings.name}:#{request.route_obj.original_path}"
67
- end
65
+ # Older versions of Padrino work with a route object
66
+ if request.respond_to?(:route_obj) && request.route_obj
67
+ return "#{settings.name}:#{request.route_obj.original_path}"
68
+ end
68
69
 
69
- # Fall back to the application name if we haven't found an action name in
70
- # any previous methods.
71
- "#{settings.name}#unknown"
70
+ # Fall back to the application name if we haven't found an action name in
71
+ # any previous methods.
72
+ "#{settings.name}#unknown"
73
+ end
74
+ end
72
75
  end
73
76
  end
74
77
 
@@ -26,9 +26,6 @@ module Appsignal
26
26
  :log_path => Rails.root.join("log")
27
27
  )
28
28
 
29
- # Start logger
30
- Appsignal.start_logger
31
-
32
29
  app.middleware.insert(
33
30
  0,
34
31
  ::Rack::Events,
@@ -71,7 +68,7 @@ module Appsignal
71
68
  transaction.set_action(action_name) if action_name
72
69
  transaction.set_metadata("path", path)
73
70
  transaction.set_metadata("method", method)
74
- transaction.params = params
71
+ transaction.set_params_if_nil(params)
75
72
  transaction.set_sample_data("custom_data", custom_data) if custom_data
76
73
 
77
74
  tags[:severity] = severity
@@ -25,7 +25,7 @@ module Appsignal
25
25
  ResqueHelpers.arguments(payload),
26
26
  Appsignal.config[:filter_parameters]
27
27
  )
28
- transaction.params = args if args
28
+ transaction.set_params_if_nil(args)
29
29
  transaction.set_tags("queue" => queue)
30
30
 
31
31
  Appsignal::Transaction.complete_current!
@@ -45,7 +45,7 @@ module Appsignal
45
45
  )
46
46
  transaction.set_action_if_nil("SidekiqInternal")
47
47
  transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
48
- transaction.params = { :jobstr => sidekiq_context[:jobstr] }
48
+ transaction.set_params_if_nil(:jobstr => sidekiq_context[:jobstr])
49
49
  transaction.set_error(exception)
50
50
  end
51
51
 
@@ -83,9 +83,7 @@ module Appsignal
83
83
  raise exception
84
84
  ensure
85
85
  if transaction
86
- params = filtered_arguments(item)
87
- transaction.params = params if params
88
-
86
+ transaction.set_params_if_nil(filtered_arguments(item))
89
87
  transaction.set_http_or_background_queue_start
90
88
  Appsignal::Transaction.complete_current! unless exception
91
89
 
@@ -12,8 +12,13 @@ unless Appsignal.active?
12
12
  app_settings.environment
13
13
  )
14
14
 
15
- Appsignal.start_logger
16
15
  Appsignal.start
17
16
  end
18
17
 
19
- ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation) if Appsignal.active?
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
@@ -25,6 +25,11 @@ module Appsignal
25
25
  Appsignal.internal_logger.debug("Initializing GVL probe")
26
26
  @appsignal = appsignal
27
27
  @gvl_tools = gvl_tools
28
+
29
+ # Store the process name and ID at initialization time
30
+ # to avoid picking up changes to the process name at runtime
31
+ @process_name = File.basename($PROGRAM_NAME).split.first || "[unknown process]"
32
+ @process_id = Process.pid
28
33
  end
29
34
 
30
35
  def call
@@ -39,13 +44,30 @@ module Appsignal
39
44
  gauge_delta :gvl_global_timer, monotonic_time_ns do |time_delta_ns|
40
45
  if time_delta_ns > 0
41
46
  time_delta_ms = time_delta_ns / 1_000_000
42
- set_gauge_with_hostname("gvl_global_timer", time_delta_ms)
47
+ set_gauges_with_hostname_and_process(
48
+ "gvl_global_timer",
49
+ time_delta_ms
50
+ )
43
51
  end
44
52
  end
45
53
  end
46
54
 
47
55
  def probe_waiting_threads
48
- set_gauge_with_hostname("gvl_waiting_threads", @gvl_tools::WaitingThreads.count)
56
+ set_gauges_with_hostname_and_process(
57
+ "gvl_waiting_threads",
58
+ @gvl_tools::WaitingThreads.count
59
+ )
60
+ end
61
+
62
+ def set_gauges_with_hostname_and_process(name, value)
63
+ set_gauge_with_hostname(name, value, {
64
+ :process_name => @process_name,
65
+ :process_id => @process_id
66
+ })
67
+
68
+ # Also set the gauge without the process name and ID for
69
+ # compatibility with existing automated dashboards
70
+ set_gauge_with_hostname(name, value)
49
71
  end
50
72
  end
51
73
  end
@@ -47,7 +47,7 @@ module Appsignal
47
47
  # @api private
48
48
  def self.dependencies_present?
49
49
  return true if sidekiq7_and_greater?
50
- return unless defined?(::Redis::VERSION) # Sidekiq <= 6
50
+ return false unless defined?(::Redis::VERSION) # Sidekiq <= 6
51
51
 
52
52
  Gem::Version.new(::Redis::VERSION) >= Gem::Version.new("3.3.5")
53
53
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- module Probes # rubocop:disable Metrics/ModuleLength
4
+ module Probes
5
5
  ITERATION_IN_SECONDS = 60
6
6
 
7
7
  class ProbeCollection
@@ -3,9 +3,11 @@
3
3
  require "rack"
4
4
 
5
5
  module Appsignal
6
- # @api private
7
6
  module Rack
7
+ # @api private
8
8
  class AbstractMiddleware
9
+ DEFAULT_ERROR_REPORTING = :default
10
+
9
11
  def initialize(app, options = {})
10
12
  Appsignal.internal_logger.debug "Initializing #{self.class}"
11
13
  @app = app
@@ -13,6 +15,7 @@ module Appsignal
13
15
  @request_class = options.fetch(:request_class, ::Rack::Request)
14
16
  @params_method = options.fetch(:params_method, :params)
15
17
  @instrument_span_name = options.fetch(:instrument_span_name, "process.abstract")
18
+ @report_errors = options.fetch(:report_errors, DEFAULT_ERROR_REPORTING)
16
19
  end
17
20
 
18
21
  def call(env)
@@ -32,15 +35,31 @@ module Appsignal
32
35
  )
33
36
  end
34
37
 
35
- add_transaction_metadata_before(transaction, request)
36
- if wrapped_instrumentation
37
- instrument_wrapped_request(request, transaction)
38
- else
38
+ unless wrapped_instrumentation
39
39
  # Set transaction on the request environment so other nested
40
40
  # middleware can detect if there is parent instrumentation
41
41
  # middleware active.
42
42
  env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
43
- instrument_request(request, transaction)
43
+ end
44
+
45
+ begin
46
+ add_transaction_metadata_before(transaction, request)
47
+ # Report errors if the :report_errors option is set to true or when
48
+ # there is no parent instrumentation that can rescue and report the error.
49
+ if @report_errors || !wrapped_instrumentation
50
+ instrument_app_call_with_exception_handling(
51
+ request.env,
52
+ transaction,
53
+ wrapped_instrumentation
54
+ )
55
+ else
56
+ instrument_app_call(request.env)
57
+ end
58
+ ensure
59
+ add_transaction_metadata_after(transaction, request)
60
+
61
+ # Complete transaction because this is the top instrumentation middleware.
62
+ Appsignal::Transaction.complete_current! unless wrapped_instrumentation
44
63
  end
45
64
  else
46
65
  @app.call(env)
@@ -56,35 +75,40 @@ module Appsignal
56
75
  # Either another {GenericInstrumentation} or {EventHandler} is higher in
57
76
  # the stack and will report the exception and complete the transaction.
58
77
  #
59
- # @see {#instrument_request}
60
- def instrument_wrapped_request(request, transaction)
61
- instrument_app_call(request.env)
62
- ensure
63
- add_transaction_metadata_after(transaction, request)
78
+ # @see {#instrument_app_call_with_exception_handling}
79
+ def instrument_app_call(env)
80
+ if @instrument_span_name
81
+ Appsignal.instrument(@instrument_span_name) do
82
+ @app.call(env)
83
+ end
84
+ else
85
+ @app.call(env)
86
+ end
64
87
  end
65
88
 
66
89
  # Instrument the request fully. This is used by the top instrumentation
67
90
  # middleware in the middleware stack. Unlike
68
- # {#instrument_wrapped_request} this will report any exceptions being
91
+ # {#instrument_app_call} this will report any exceptions being
69
92
  # raised.
70
93
  #
71
- # @see {#instrument_wrapped_request}
72
- def instrument_request(request, transaction)
73
- instrument_app_call(request.env)
94
+ # @see {#instrument_app_call}
95
+ def instrument_app_call_with_exception_handling(env, transaction, wrapped_instrumentation)
96
+ instrument_app_call(env)
74
97
  rescue Exception => error # rubocop:disable Lint/RescueException
75
- transaction.set_error(error)
98
+ report_errors =
99
+ if @report_errors == DEFAULT_ERROR_REPORTING
100
+ # If there's no parent transaction, report the error
101
+ !wrapped_instrumentation
102
+ elsif @report_errors.respond_to?(:call)
103
+ # If the @report_errors option is callable, call it with the
104
+ # request environment so it can determine if the error needs to be
105
+ # reported.
106
+ @report_errors.call(env)
107
+ else
108
+ @report_errors
109
+ end
110
+ transaction.set_error(error) if report_errors
76
111
  raise error
77
- ensure
78
- add_transaction_metadata_after(transaction, request)
79
-
80
- # Complete transaction because this is the top instrumentation middleware.
81
- Appsignal::Transaction.complete_current!
82
- end
83
-
84
- def instrument_app_call(env)
85
- Appsignal.instrument(@instrument_span_name) do
86
- @app.call(env)
87
- end
88
112
  end
89
113
 
90
114
  # Add metadata to the transaction based on the request environment.
@@ -101,7 +125,10 @@ module Appsignal
101
125
  request.env["appsignal.route"] || request.env["appsignal.action"]
102
126
  transaction.set_action_if_nil(default_action)
103
127
  transaction.set_metadata("path", request.path)
104
- transaction.set_metadata("method", request.request_method)
128
+
129
+ request_method = request_method_for(request)
130
+ transaction.set_metadata("method", request_method) if request_method
131
+
105
132
  transaction.set_params_if_nil(params_for(request))
106
133
  transaction.set_http_or_background_queue_start
107
134
  end
@@ -118,6 +145,13 @@ module Appsignal
118
145
  nil
119
146
  end
120
147
 
148
+ def request_method_for(request)
149
+ request.request_method
150
+ rescue => error
151
+ Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
152
+ nil
153
+ end
154
+
121
155
  def request_for(env)
122
156
  @request_class.new(env)
123
157
  end
@@ -4,8 +4,10 @@ module Appsignal
4
4
  module Rack
5
5
  APPSIGNAL_TRANSACTION = "appsignal.transaction"
6
6
  APPSIGNAL_EVENT_HANDLER_ID = "appsignal.event_handler_id"
7
+ APPSIGNAL_EVENT_HANDLER_HAS_ERROR = "appsignal.event_handler.error"
7
8
  RACK_AFTER_REPLY = "rack.after_reply"
8
9
 
10
+ # @api private
9
11
  class EventHandler
10
12
  include ::Rack::Events::Abstract
11
13
 
@@ -42,22 +44,22 @@ module Appsignal
42
44
 
43
45
  request.env[RACK_AFTER_REPLY] ||= []
44
46
  request.env[RACK_AFTER_REPLY] << proc do
47
+ next unless event_handler.request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
48
+
45
49
  Appsignal::Rack::EventHandler
46
50
  .safe_execution("Appsignal::Rack::EventHandler's after_reply") do
47
- next unless event_handler.request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
48
-
49
51
  transaction.finish_event("process_request.rack", "", "")
50
52
  transaction.set_http_or_background_queue_start
51
-
52
- # Make sure the current transaction is always closed when the request
53
- # is finished. This is a fallback for in case the `on_finish`
54
- # callback is not called. This is supported by servers like Puma and
55
- # Unicorn.
56
- #
57
- # The EventHandler.on_finish callback should be called first, this is
58
- # just a fallback if that doesn't get called.
59
- Appsignal::Transaction.complete_current!
60
53
  end
54
+
55
+ # Make sure the current transaction is always closed when the request
56
+ # is finished. This is a fallback for in case the `on_finish`
57
+ # callback is not called. This is supported by servers like Puma and
58
+ # Unicorn.
59
+ #
60
+ # The EventHandler.on_finish callback should be called first, this is
61
+ # just a fallback if that doesn't get called.
62
+ Appsignal::Transaction.complete_current!
61
63
  end
62
64
  transaction.start_event
63
65
  end
@@ -70,31 +72,40 @@ module Appsignal
70
72
  transaction = request.env[APPSIGNAL_TRANSACTION]
71
73
  return unless transaction
72
74
 
75
+ request.env[APPSIGNAL_EVENT_HANDLER_HAS_ERROR] = true
73
76
  transaction.set_error(error)
74
77
  end
75
78
  end
76
79
 
77
80
  def on_finish(request, response)
78
- self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
79
- return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
81
+ return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
80
82
 
81
- transaction = request.env[APPSIGNAL_TRANSACTION]
82
- return unless transaction
83
+ transaction = request.env[APPSIGNAL_TRANSACTION]
84
+ return unless transaction
83
85
 
86
+ self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
84
87
  transaction.finish_event("process_request.rack", "", "")
85
- transaction.set_tags(:response_status => response.status)
86
88
  transaction.set_http_or_background_queue_start
87
- Appsignal.increment_counter(
88
- :response_status,
89
- 1,
90
- :status => response.status,
91
- :namespace => format_namespace(transaction.namespace)
92
- )
93
-
94
- # Make sure the current transaction is always closed when the request
95
- # is finished
96
- Appsignal::Transaction.complete_current!
89
+ response_status =
90
+ if response
91
+ response.status
92
+ elsif request.env[APPSIGNAL_EVENT_HANDLER_HAS_ERROR] == true
93
+ 500
94
+ end
95
+ if response_status
96
+ transaction.set_tags(:response_status => response_status)
97
+ Appsignal.increment_counter(
98
+ :response_status,
99
+ 1,
100
+ :status => response_status,
101
+ :namespace => format_namespace(transaction.namespace)
102
+ )
103
+ end
97
104
  end
105
+
106
+ # Make sure the current transaction is always closed when the request
107
+ # is finished
108
+ Appsignal::Transaction.complete_current!
98
109
  end
99
110
 
100
111
  private
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Rack
5
+ # @api private
6
+ class GrapeMiddleware < Appsignal::Rack::AbstractMiddleware
7
+ def initialize(app, options = {})
8
+ options[:instrument_span_name] = "process_request.grape"
9
+ options[:report_errors] = lambda { |env| !env["grape.skip_appsignal_error"] }
10
+ super
11
+ end
12
+
13
+ private
14
+
15
+ def add_transaction_metadata_after(transaction, request)
16
+ endpoint = request.env["api.endpoint"]
17
+ unless endpoint&.options
18
+ super
19
+ return
20
+ end
21
+
22
+ options = endpoint.options
23
+ request_method = options[:method].first.to_s.upcase
24
+ klass = options[:for]
25
+ namespace = endpoint.namespace
26
+ namespace = "" if namespace == "/"
27
+
28
+ path = options[:path].first.to_s
29
+ path = "/#{path}" if path[0] != "/"
30
+ path = "#{namespace}#{path}"
31
+
32
+ transaction.set_action_if_nil("#{request_method}::#{klass}##{path}")
33
+
34
+ super
35
+
36
+ transaction.set_metadata("path", path)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Rack
5
+ # @api private
6
+ class HanamiMiddleware < AbstractMiddleware
7
+ def initialize(app, options = {})
8
+ options[:params_method] ||= :params
9
+ options[:instrument_span_name] ||= "process_action.hanami"
10
+ super
11
+ end
12
+
13
+ private
14
+
15
+ def params_for(request)
16
+ ::Hanami::Action.params_class.new(request.env).to_h
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,69 +3,27 @@
3
3
  require "rack"
4
4
 
5
5
  module Appsignal
6
- # @api private
7
6
  module Rack
8
- class RailsInstrumentation
7
+ # @api private
8
+ class RailsInstrumentation < Appsignal::Rack::AbstractMiddleware
9
9
  def initialize(app, options = {})
10
- Appsignal.internal_logger.debug "Initializing Appsignal::Rack::RailsInstrumentation"
11
- @app = app
12
- @options = options
10
+ options[:request_class] ||= ActionDispatch::Request
11
+ options[:params_method] ||= :filtered_parameters
12
+ options[:instrument_span_name] = nil
13
+ options[:report_errors] = true
14
+ super
13
15
  end
14
16
 
15
- def call(env)
16
- if Appsignal.active?
17
- call_with_appsignal_monitoring(env)
18
- else
19
- @app.call(env)
20
- end
21
- end
22
-
23
- def call_with_appsignal_monitoring(env)
24
- request = ActionDispatch::Request.new(env)
25
- transaction = env.fetch(
26
- Appsignal::Rack::APPSIGNAL_TRANSACTION,
27
- Appsignal::Transaction::NilTransaction.new
28
- )
29
-
30
- begin
31
- transaction.params = fetch_params(request)
32
-
33
- @app.call(env)
34
- rescue Exception => error # rubocop:disable Lint/RescueException
35
- transaction.set_error(error)
36
- raise error
37
- ensure
38
- controller = env["action_controller.instance"]
39
- if controller
40
- transaction.set_action_if_nil("#{controller.class}##{controller.action_name}")
41
- end
42
- request_id = fetch_request_id(env)
43
- transaction.set_tags(:request_id => request_id) if request_id
44
- transaction.set_metadata("path", request.path)
45
- request_method = fetch_request_method(request)
46
- transaction.set_metadata("method", request_method) if request_method
47
- end
48
- end
49
-
50
- def fetch_request_id(env)
51
- env["action_dispatch.request_id"]
52
- end
17
+ private
53
18
 
54
- def fetch_params(request)
55
- return unless request.respond_to?(:filtered_parameters)
19
+ def add_transaction_metadata_after(transaction, request)
20
+ controller = request.env["action_controller.instance"]
21
+ transaction.set_action_if_nil("#{controller.class}##{controller.action_name}") if controller
56
22
 
57
- request.filtered_parameters
58
- rescue => error
59
- # Getting params from the request has been know to fail.
60
- Appsignal.internal_logger.debug "Exception while getting Rails params: #{error}"
61
- nil
62
- end
23
+ request_id = request.env["action_dispatch.request_id"]
24
+ transaction.set_tags(:request_id => request_id) if request_id
63
25
 
64
- def fetch_request_method(request)
65
- request.request_method
66
- rescue => error
67
- Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
68
- nil
26
+ super
69
27
  end
70
28
  end
71
29
  end