appsignal 3.9.1-java → 3.9.3-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 (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