appsignal 3.9.0-java → 3.9.2-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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87ae30cedc24d783bcd6f28bb01bb19384e0f7a280fe2c8b89585a5161aaaf77
4
- data.tar.gz: 36c1760ff247804c579bb0b7bf3db2aee1d4ed53ede1a1920d5a8a1fa762f032
3
+ metadata.gz: d8242c596a38a842614a47cb94b88a666de659fad2b0c5270dc94747c0ec996c
4
+ data.tar.gz: e604b5e5acd710f2978579db85b3e274461d4c2c3b68065c9e4541b2a7f4b133
5
5
  SHA512:
6
- metadata.gz: ed40403d826734ba4725b087b4d52d75be79d9bfd7172d12ceba70da3393de9bf55ca02964a81104082e9058eb44d1c3795623a21b244b13905a0310f2961d16
7
- data.tar.gz: 66832484c634de15a474774f49e8c38838faf4b71889c68c4ad7ef897d1ad64c119b926152a228117409103b657f8523599597394b989bbf87553d1c072bf5a1
6
+ metadata.gz: 682a946ea99d3db5d552ad1c6afa1ed67493c71427dfd55172a03349f6dfdbbaaabf6459123c19598e6c66164293cd845431264e7b6d832b5cd40b870c642f67
7
+ data.tar.gz: ce4f0c6fdd313528c2b91ad3b0c4c50f3e05bb93b56d2f2a983da830ff4a0dfa8a1184ce6b8cad0c20091b7e1d07dedea4f259f8e528ecc86295b8ea3a375e0b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.9.2
4
+
5
+ _Published on 2024-06-26._
6
+
7
+ ### Added
8
+
9
+ - Improve instrumentation of Hanami requests by making sure the transaction is always closed.
10
+ It will also report a `response_status` tag and metric for Hanami requests.
11
+
12
+ (patch [e79d4277](https://github.com/appsignal/appsignal-ruby/commit/e79d4277046bf4ec0d32263d06d4975ca8c426ee))
13
+
14
+ ### Changed
15
+
16
+ - Instrument the entire Sinatra request. Instrumenting Sinatra apps using `require "appsignal/integrations/sinatra"` will now report more of the request, if previously other middleware were not instrumented. It will also report the response status with the `response_status` tag and metric. (patch [15b3390b](https://github.com/appsignal/appsignal-ruby/commit/15b3390b5b54cdc7378d69c92d91ec51dab1b0e4))
17
+
18
+ ### Fixed
19
+
20
+ - Fix deprecation warnings about `Transacation.params=` usage by updating how we record parameters in our instrumentations. (patch [b65d6674](https://github.com/appsignal/appsignal-ruby/commit/b65d6674c93afbc95e9cecee8c032e6949229aab))
21
+ - Fix error reporting for requests with an error that use the AppSignal EventHandler. (patch [0e48f19b](https://github.com/appsignal/appsignal-ruby/commit/0e48f19bb9f5c3ead96d21fbacdd5d7f221e2063))
22
+
23
+ ## 3.9.1
24
+
25
+ _Published on 2024-06-24._
26
+
27
+ ### Fixed
28
+
29
+ - [0a253aa1](https://github.com/appsignal/appsignal-ruby/commit/0a253aa16c00cd6172e35a4edaff34f76ac9cbe5) patch - Fix parameter reporting for Rack and Sinatra apps, especially POST payloads.
30
+
3
31
  ## 3.9.0
4
32
 
5
33
  _Published on 2024-06-21._
@@ -176,7 +176,7 @@ module Appsignal
176
176
  #
177
177
  # @example Add more metadata to transaction
178
178
  # Appsignal.send_error(e) do |transaction|
179
- # transaction.params = { :search_query => params[:search_query] }
179
+ # transaction.set_params(:search_query => params[:search_query])
180
180
  # transaction.set_action("my_action_name")
181
181
  # transaction.set_tags(:key => "value")
182
182
  # transaction.set_namespace("my_namespace")
@@ -273,7 +273,7 @@ module Appsignal
273
273
  #
274
274
  # @example Add more metadata to transaction
275
275
  # Appsignal.set_error(e) do |transaction|
276
- # transaction.params = { :search_query => params[:search_query] }
276
+ # transaction.set_params(:search_query => params[:search_query])
277
277
  # transaction.set_action("my_action_name")
278
278
  # transaction.set_tags(:key => "value")
279
279
  # transaction.set_namespace("my_namespace")
@@ -62,11 +62,12 @@ module Appsignal
62
62
  end
63
63
 
64
64
  if transaction
65
- transaction.params =
65
+ transaction.set_params_if_nil(
66
66
  Appsignal::Utils::HashSanitizer.sanitize(
67
67
  job["arguments"],
68
68
  Appsignal.config[:filter_parameters]
69
69
  )
70
+ )
70
71
 
71
72
  transaction_tags = ActiveJobHelpers.transaction_tags_for(job)
72
73
  transaction_tags["active_job_id"] = job["job_id"]
@@ -22,7 +22,7 @@ module Appsignal
22
22
  transaction.set_error(exception)
23
23
  raise exception
24
24
  ensure
25
- transaction.params = args.first
25
+ transaction.set_params_if_nil(args.first)
26
26
  transaction.set_action_if_nil("#{self.class}##{args.first["action"]}")
27
27
  transaction.set_metadata("path", request.path)
28
28
  transaction.set_metadata("method", "websocket")
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "appsignal"
4
+ require "appsignal/rack/hanami_middleware"
4
5
 
5
6
  module Appsignal
6
7
  module Integrations
8
+ # @api private
7
9
  module HanamiPlugin
8
10
  def self.init
9
11
  Appsignal.internal_logger.debug("Loading Hanami integration")
@@ -17,44 +19,27 @@ module Appsignal
17
19
  Appsignal.start_logger
18
20
  Appsignal.start
19
21
 
20
- ::Hanami::Action.prepend Appsignal::Integrations::HanamiIntegration if Appsignal.active?
22
+ return unless Appsignal.active?
23
+
24
+ hanami_app_config.middleware.use(
25
+ ::Rack::Events,
26
+ [Appsignal::Rack::EventHandler.new]
27
+ )
28
+ hanami_app_config.middleware.use(Appsignal::Rack::HanamiMiddleware)
29
+
30
+ ::Hanami::Action.prepend Appsignal::Integrations::HanamiIntegration
21
31
  end
22
32
  end
23
- end
24
- end
25
33
 
26
- module Appsignal::Integrations::HanamiIntegration
27
- def call(env)
28
- params = ::Hanami::Action::BaseParams.new(env)
29
- request = ::Hanami::Action::Request.new(
30
- :env => env,
31
- :params => params,
32
- :sessions_enabled => true
33
- )
34
-
35
- transaction = Appsignal::Transaction.create(
36
- SecureRandom.uuid,
37
- Appsignal::Transaction::HTTP_REQUEST,
38
- request
39
- )
40
-
41
- begin
42
- Appsignal.instrument("process_action.hanami") do
43
- super.tap do |response|
44
- transaction.set_metadata("status", response.status.to_s)
45
- end
34
+ # @api private
35
+ module HanamiIntegration
36
+ def call(env)
37
+ super
38
+ ensure
39
+ transaction = env[::Appsignal::Rack::APPSIGNAL_TRANSACTION]
40
+
41
+ transaction&.set_action_if_nil(self.class.name)
46
42
  end
47
- rescue Exception => error # rubocop:disable Lint/RescueException
48
- transaction.set_error(error)
49
- transaction.set_metadata("status", "500")
50
- raise error
51
- ensure
52
- transaction.params = request.params.to_h
53
- transaction.set_action_if_nil(self.class.name)
54
- transaction.set_metadata("path", request.path)
55
- transaction.set_metadata("method", request.request_method)
56
- transaction.set_http_or_background_queue_start
57
- Appsignal::Transaction.complete_current!
58
43
  end
59
44
  end
60
45
  end
@@ -71,7 +71,7 @@ module Appsignal
71
71
  transaction.set_action(action_name) if action_name
72
72
  transaction.set_metadata("path", path)
73
73
  transaction.set_metadata("method", method)
74
- transaction.params = params
74
+ transaction.set_params_if_nil(params)
75
75
  transaction.set_sample_data("custom_data", custom_data) if custom_data
76
76
 
77
77
  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
 
@@ -16,4 +16,10 @@ unless Appsignal.active?
16
16
  Appsignal.start
17
17
  end
18
18
 
19
- ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation) if Appsignal.active?
19
+ if Appsignal.active?
20
+ ::Sinatra::Base.use(
21
+ ::Rack::Events,
22
+ [Appsignal::Rack::EventHandler.new]
23
+ )
24
+ ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
25
+ end
@@ -91,8 +91,6 @@ module Appsignal
91
91
  # Override this method to set metadata before the app is called.
92
92
  # Call `super` to also include the default set metadata.
93
93
  def add_transaction_metadata_before(transaction, request)
94
- params = params_for(request)
95
- transaction.params = params if params
96
94
  end
97
95
 
98
96
  # Add metadata to the transaction based on the request environment.
@@ -104,6 +102,7 @@ module Appsignal
104
102
  transaction.set_action_if_nil(default_action)
105
103
  transaction.set_metadata("path", request.path)
106
104
  transaction.set_metadata("method", request.request_method)
105
+ transaction.set_params_if_nil(params_for(request))
107
106
  transaction.set_http_or_background_queue_start
108
107
  end
109
108
 
@@ -42,22 +42,22 @@ module Appsignal
42
42
 
43
43
  request.env[RACK_AFTER_REPLY] ||= []
44
44
  request.env[RACK_AFTER_REPLY] << proc do
45
+ next unless event_handler.request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
46
+
45
47
  Appsignal::Rack::EventHandler
46
48
  .safe_execution("Appsignal::Rack::EventHandler's after_reply") do
47
- next unless event_handler.request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
48
-
49
49
  transaction.finish_event("process_request.rack", "", "")
50
50
  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
51
  end
52
+
53
+ # Make sure the current transaction is always closed when the request
54
+ # is finished. This is a fallback for in case the `on_finish`
55
+ # callback is not called. This is supported by servers like Puma and
56
+ # Unicorn.
57
+ #
58
+ # The EventHandler.on_finish callback should be called first, this is
59
+ # just a fallback if that doesn't get called.
60
+ Appsignal::Transaction.complete_current!
61
61
  end
62
62
  transaction.start_event
63
63
  end
@@ -75,26 +75,28 @@ module Appsignal
75
75
  end
76
76
 
77
77
  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])
78
+ return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
80
79
 
81
- transaction = request.env[APPSIGNAL_TRANSACTION]
82
- return unless transaction
80
+ transaction = request.env[APPSIGNAL_TRANSACTION]
81
+ return unless transaction
83
82
 
83
+ self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
84
84
  transaction.finish_event("process_request.rack", "", "")
85
- transaction.set_tags(:response_status => response.status)
86
85
  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!
86
+ if response
87
+ transaction.set_tags(:response_status => response.status)
88
+ Appsignal.increment_counter(
89
+ :response_status,
90
+ 1,
91
+ :status => response.status,
92
+ :namespace => format_namespace(transaction.namespace)
93
+ )
94
+ end
97
95
  end
96
+
97
+ # Make sure the current transaction is always closed when the request
98
+ # is finished
99
+ Appsignal::Transaction.complete_current!
98
100
  end
99
101
 
100
102
  private
@@ -0,0 +1,30 @@
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[:request_class] ||= ::Hanami::Action::Request
9
+ options[:params_method] ||= :params
10
+ options[:instrument_span_name] ||= "process_action.hanami"
11
+ super
12
+ end
13
+
14
+ private
15
+
16
+ def params_for(request)
17
+ super&.to_h
18
+ end
19
+
20
+ def request_for(env)
21
+ params = ::Hanami::Action.params_class.new(env)
22
+ @request_class.new(
23
+ :env => env,
24
+ :params => params,
25
+ :sessions_enabled => true
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -28,8 +28,6 @@ module Appsignal
28
28
  )
29
29
 
30
30
  begin
31
- transaction.params = fetch_params(request)
32
-
33
31
  @app.call(env)
34
32
  rescue Exception => error # rubocop:disable Lint/RescueException
35
33
  transaction.set_error(error)
@@ -39,6 +37,7 @@ module Appsignal
39
37
  if controller
40
38
  transaction.set_action_if_nil("#{controller.class}##{controller.action_name}")
41
39
  end
40
+ transaction.set_params_if_nil(fetch_params(request))
42
41
  request_id = fetch_request_id(env)
43
42
  transaction.set_tags(:request_id => request_id) if request_id
44
43
  transaction.set_metadata("path", request.path)
@@ -76,18 +76,6 @@ module Appsignal
76
76
  attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options,
77
77
  :discarded, :breadcrumbs
78
78
 
79
- # @!attribute params
80
- # Attribute for parameters of the transaction.
81
- #
82
- # When no parameters are set with {#params=} the parameters it will look
83
- # for parameters on the {#request} environment.
84
- #
85
- # The parameters set using {#params=} are leading over those extracted
86
- # from a request's environment.
87
- #
88
- # @return [Hash]
89
- attr_writer :params
90
-
91
79
  def initialize(transaction_id, namespace, request, options = {})
92
80
  @transaction_id = transaction_id
93
81
  @action = nil
@@ -156,6 +144,40 @@ module Appsignal
156
144
  request_params
157
145
  end
158
146
 
147
+ # Set parameters on the transaction.
148
+ #
149
+ # When no parameters are set this way, the transaction will look for
150
+ # parameters on the {#request} environment.
151
+ #
152
+ # The parameters set using {#set_params} are leading over those extracted
153
+ # from a request's environment.
154
+ #
155
+ # @param given_params [Hash] The parameters to set on the transaction.
156
+ # @return [void]
157
+ def set_params(given_params)
158
+ @params = given_params if given_params
159
+ end
160
+
161
+ # @deprecated Use {#set_params} or {#set_params_if_nil} instead.
162
+ def params=(given_params)
163
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
164
+ "Transaction#params= is deprecated." \
165
+ "Use Transaction#set_params or #set_params_if_nil instead."
166
+ )
167
+ set_params(given_params)
168
+ end
169
+
170
+ # Set parameters on the transaction if not already set
171
+ #
172
+ # When no parameters are set this way, the transaction will look for
173
+ # parameters on the {#request} environment.
174
+ #
175
+ # @param given_params [Hash] The parameters to set on the transaction if none are already set.
176
+ # @return [void]
177
+ def set_params_if_nil(given_params)
178
+ set_params(given_params) unless @params
179
+ end
180
+
159
181
  # Set tags on the transaction.
160
182
  #
161
183
  # @param given_tags [Hash] Collection of tags.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.9.0"
4
+ VERSION = "3.9.2"
5
5
  end
@@ -5,32 +5,62 @@ if DependencyHelper.hanami2_present?
5
5
  require "appsignal/integrations/hanami"
6
6
 
7
7
  before do
8
- allow(Appsignal).to receive(:active?).and_return(true)
9
- allow(Appsignal).to receive(:start).and_return(true)
10
- allow(Appsignal).to receive(:start_logger).and_return(true)
8
+ uninstall_hanami_middleware
9
+ end
10
+
11
+ def uninstall_hanami_middleware
12
+ middleware_stack = ::Hanami.app.config.middleware.stack[::Hanami::Router::DEFAULT_PREFIX]
13
+ middleware_stack.delete_if do |middleware|
14
+ middleware.first == Appsignal::Rack::HanamiMiddleware ||
15
+ middleware.first == Rack::Events
16
+ end
11
17
  end
12
18
 
13
19
  describe Appsignal::Integrations::HanamiPlugin do
14
20
  it "starts AppSignal on init" do
15
21
  expect(Appsignal).to receive(:start)
22
+ expect(Appsignal).to receive(:start_logger)
23
+ Appsignal::Integrations::HanamiPlugin.init
16
24
  end
17
25
 
18
- it "starts the logger on init" do
19
- expect(Appsignal).to receive(:start_logger)
26
+ it "prepends the integration to Hanami::Action" do
27
+ allow(Appsignal).to receive(:active?).and_return(true)
28
+ Appsignal::Integrations::HanamiPlugin.init
29
+ expect(::Hanami::Action.included_modules)
30
+ .to include(Appsignal::Integrations::HanamiIntegration)
20
31
  end
21
32
 
22
- it "prepends the integration to Hanami" do
23
- expect(::Hanami::Action).to receive(:prepend)
24
- .with(Appsignal::Integrations::HanamiIntegration)
33
+ it "adds middleware to the Hanami app" do
34
+ allow(Appsignal).to receive(:active?).and_return(true)
35
+ Appsignal::Integrations::HanamiPlugin.init
36
+
37
+ expect(::Hanami.app.config.middleware.stack[::Hanami::Router::DEFAULT_PREFIX])
38
+ .to include(
39
+ [Rack::Events, [[kind_of(Appsignal::Rack::EventHandler)]], nil],
40
+ [Appsignal::Rack::HanamiMiddleware, [], nil]
41
+ )
25
42
  end
26
43
 
27
44
  context "when not active" do
28
45
  before { allow(Appsignal).to receive(:active?).and_return(false) }
29
46
 
30
- it "does not prepend the integration" do
47
+ it "does not prepend the integration to Hanami::Action" do
48
+ Appsignal::Integrations::HanamiPlugin.init
31
49
  expect(::Hanami::Action).to_not receive(:prepend)
32
50
  .with(Appsignal::Integrations::HanamiIntegration)
33
51
  end
52
+
53
+ it "does not add the middleware to the Hanami app" do
54
+ Appsignal::Integrations::HanamiPlugin.init
55
+
56
+ middleware_stack = ::Hanami.app.config.middleware.stack[::Hanami::Router::DEFAULT_PREFIX]
57
+ expect(middleware_stack).to_not include(
58
+ [Rack::Events, [[kind_of(Appsignal::Rack::EventHandler)]], nil]
59
+ )
60
+ expect(middleware_stack).to_not include(
61
+ [Appsignal::Rack::HanamiMiddleware, [], nil]
62
+ )
63
+ end
34
64
  end
35
65
 
36
66
  context "when APPSIGNAL_APP_ENV ENV var is provided" do
@@ -52,71 +82,45 @@ if DependencyHelper.hanami2_present?
52
82
  expect(Appsignal.config.env).to eq("test")
53
83
  end
54
84
  end
55
-
56
- after { Appsignal::Integrations::HanamiPlugin.init }
57
85
  end
58
86
 
59
- describe "Hanami Actions" do
60
- let(:env) do
61
- Rack::MockRequest.env_for(
62
- "/books",
63
- "router.params" => router_params,
64
- :method => "GET"
65
- )
87
+ describe Appsignal::Integrations::HanamiIntegration do
88
+ let(:transaction) { http_request_transaction }
89
+ around { |example| keep_transactions { example.run } }
90
+ before(:context) { start_agent }
91
+ before do
92
+ allow(Appsignal).to receive(:active?).and_return(true)
93
+ Appsignal::Integrations::HanamiPlugin.init
66
94
  end
67
95
 
68
- let(:router_params) { { :foo => "bar", :baz => "qux" } }
69
-
70
- describe "#call", :error => false do
71
- it "sets params" do
72
- expect_any_instance_of(Appsignal::Transaction).to receive(:params=).with(router_params)
73
- end
74
-
75
- it "sets the action name" do
76
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil)
77
- .with("HanamiApp::Actions::Books::Index")
78
- end
79
-
80
- it "sets the metadata" do
81
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
82
- .with("status", "200")
83
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
84
- .with("path", "/books")
85
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
86
- .with("method", "GET")
87
- end
96
+ def make_request(env, app: HanamiApp::Actions::Books::Index)
97
+ action = app.new
98
+ action.call(env)
99
+ end
88
100
 
89
- it "sets the queue start" do
90
- expect_any_instance_of(Appsignal::Transaction)
91
- .to receive(:set_http_or_background_queue_start)
92
- end
101
+ describe "#call" do
102
+ context "without an active transaction" do
103
+ let(:env) { {} }
93
104
 
94
- context "with error", :error => true do
95
- let(:error) { HanamiApp::ExampleError }
105
+ it "does not set the action name" do
106
+ make_request(env)
96
107
 
97
- it "records the exception" do
98
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
99
- end
100
-
101
- it "sets the status to 500" do
102
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
103
- .with("status", "500")
104
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).twice
108
+ expect(transaction.to_h).to include(
109
+ "action" => nil
110
+ )
105
111
  end
106
112
  end
107
113
 
108
- after(:error => false) do
109
- Appsignal::Integrations::HanamiPlugin.init
110
-
111
- action = HanamiApp::Actions::Books::Index.new
112
- action.call(env)
113
- end
114
+ context "with an active transaction" do
115
+ let(:env) { { Appsignal::Rack::APPSIGNAL_TRANSACTION => transaction } }
114
116
 
115
- after(:error => true) do
116
- Appsignal::Integrations::HanamiPlugin.init
117
+ it "sets action name on the transaction" do
118
+ make_request(env)
117
119
 
118
- action = HanamiApp::Actions::Books::Error.new
119
- expect { action.call(env) }.to raise_error(error)
120
+ expect(transaction.to_h).to include(
121
+ "action" => "HanamiApp::Actions::Books::Index"
122
+ )
123
+ end
120
124
  end
121
125
  end
122
126
  end
@@ -50,9 +50,13 @@ if DependencyHelper.sinatra_present?
50
50
  context "when AppSignal is not active" do
51
51
  it "does not add the instrumentation middleware to Sinatra::Base" do
52
52
  install_sinatra_integration
53
- expect(Sinatra::Base.middleware.to_a).to_not include(
53
+ middlewares = Sinatra::Base.middleware.to_a
54
+ expect(middlewares).to_not include(
54
55
  [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
55
56
  )
57
+ expect(middlewares).to_not include(
58
+ [Rack::Events, [Appsignal::Rack::EventHandler], nil]
59
+ )
56
60
  end
57
61
  end
58
62
 
@@ -63,7 +67,9 @@ if DependencyHelper.sinatra_present?
63
67
  ENV["APPSIGNAL_PUSH_API_KEY"] = "my-key"
64
68
 
65
69
  install_sinatra_integration
66
- expect(Sinatra::Base.middleware.to_a).to include(
70
+ middlewares = Sinatra::Base.middleware.to_a
71
+ expect(middlewares).to include(
72
+ [Rack::Events, [[Appsignal::Rack::EventHandler]], nil],
67
73
  [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
68
74
  )
69
75
  end
@@ -66,6 +66,30 @@ describe Appsignal::Rack::EventHandler do
66
66
  expect(last_transaction.ext.queue_start).to eq(queue_start_time)
67
67
  end
68
68
 
69
+ context "with error inside rack.after_reply handler" do
70
+ before do
71
+ on_start
72
+ # A random spot we can access to raise an error for this test
73
+ expect(request.env[Appsignal::Rack::APPSIGNAL_TRANSACTION])
74
+ .to receive(:finish_event)
75
+ .and_raise(ExampleStandardError, "oh no")
76
+ callback = request.env[Appsignal::Rack::RACK_AFTER_REPLY].first
77
+ callback.call
78
+ end
79
+
80
+ it "completes the transaction" do
81
+ expect(last_transaction).to be_completed
82
+ end
83
+
84
+ it "logs an error" do
85
+ expect(log).to contains_log(
86
+ :error,
87
+ "Error occurred in Appsignal::Rack::EventHandler's after_reply: " \
88
+ "ExampleStandardError: oh no"
89
+ )
90
+ end
91
+ end
92
+
69
93
  it "logs errors from rack.after_reply callbacks" do
70
94
  on_start
71
95
 
@@ -139,8 +163,8 @@ describe Appsignal::Rack::EventHandler do
139
163
  describe "#on_finish" do
140
164
  let(:response) { Rack::Events::BufferedResponse.new(200, {}, ["body"]) }
141
165
 
142
- def on_finish
143
- event_handler_instance.on_finish(request, response)
166
+ def on_finish(given_request = request, given_response = response)
167
+ event_handler_instance.on_finish(given_request, given_response)
144
168
  end
145
169
 
146
170
  it "doesn't do anything without a transaction" do
@@ -155,6 +179,7 @@ describe Appsignal::Rack::EventHandler do
155
179
  "sample_data" => {},
156
180
  "events" => []
157
181
  )
182
+ expect(last_transaction).to_not be_completed
158
183
  end
159
184
 
160
185
  it "completes the transaction" do
@@ -176,6 +201,60 @@ describe Appsignal::Rack::EventHandler do
176
201
  expect(last_transaction).to be_completed
177
202
  end
178
203
 
204
+ context "without a response" do
205
+ it "completes the transaction" do
206
+ on_start
207
+ on_finish(request, nil)
208
+
209
+ expect(last_transaction.to_h).to include(
210
+ # The action is not set on purpose, as we can't set a normalized route
211
+ # It requires the app to set an action name
212
+ "action" => nil,
213
+ "sample_data" => hash_including(
214
+ "environment" => {
215
+ "REQUEST_METHOD" => "GET",
216
+ "PATH_INFO" => "/path"
217
+ }
218
+ )
219
+ )
220
+ expect(last_transaction.ext.queue_start).to eq(queue_start_time)
221
+ expect(last_transaction).to be_completed
222
+ end
223
+
224
+ it "does not set a response_status tag" do
225
+ on_start
226
+ on_finish(request, nil)
227
+
228
+ expect(last_transaction.to_h.dig("sample_data", "tags")).to_not have_key("response_status")
229
+ end
230
+
231
+ it "does not report a response_status counter metric" do
232
+ expect(Appsignal).to_not receive(:increment_counter)
233
+ .with(:response_status, anything, anything)
234
+
235
+ on_start
236
+ on_finish(request, nil)
237
+ end
238
+ end
239
+
240
+ context "with error inside on_finish handler" do
241
+ before do
242
+ on_start
243
+ # A random spot we can access to raise an error for this test
244
+ expect(Appsignal).to receive(:increment_counter).and_raise(ExampleStandardError, "oh no")
245
+ on_finish
246
+ end
247
+
248
+ it "completes the transaction" do
249
+ expect(last_transaction).to be_completed
250
+ end
251
+
252
+ it "logs an error" do
253
+ expect(log).to contains_log(:error,
254
+ "Error occurred in Appsignal::Rack::EventHandler#on_finish: ExampleStandardError: oh no")
255
+ end
256
+ end
257
+
179
258
  context "when the handler is nested in another EventHandler" do
180
259
  it "does not complete the transaction" do
181
260
  on_start
@@ -237,8 +316,8 @@ describe Appsignal::Rack::EventHandler do
237
316
  end
238
317
 
239
318
  it "logs an error in case of an error" do
240
- expect(Appsignal::Transaction)
241
- .to receive(:complete_current!).and_raise(ExampleStandardError, "oh no")
319
+ # A random spot we can access to raise an error for this test
320
+ expect(Appsignal).to receive(:increment_counter).and_raise(ExampleStandardError, "oh no")
242
321
 
243
322
  on_start
244
323
  on_finish
@@ -0,0 +1,50 @@
1
+ require "appsignal/rack/hanami_middleware"
2
+
3
+ if DependencyHelper.hanami2_present?
4
+ describe Appsignal::Rack::HanamiMiddleware do
5
+ let(:app) { double(:call => true) }
6
+ let(:router_params) { { "param1" => "value1", "param2" => "value2" } }
7
+ let(:env) do
8
+ Rack::MockRequest.env_for(
9
+ "/some/path",
10
+ "router.params" => router_params
11
+ )
12
+ end
13
+ let(:middleware) { Appsignal::Rack::HanamiMiddleware.new(app, {}) }
14
+
15
+ before(:context) { start_agent }
16
+ around { |example| keep_transactions { example.run } }
17
+
18
+ def make_request(env)
19
+ middleware.call(env)
20
+ end
21
+
22
+ context "with params" do
23
+ it "sets request parameters on the transaction" do
24
+ make_request(env)
25
+
26
+ expect(last_transaction.to_h).to include(
27
+ "sample_data" => hash_including(
28
+ "params" => { "param1" => "value1", "param2" => "value2" }
29
+ )
30
+ )
31
+ end
32
+ end
33
+
34
+ it "reports a process_action.hanami event" do
35
+ make_request(env)
36
+
37
+ expect(last_transaction.to_h).to include(
38
+ "events" => [
39
+ hash_including(
40
+ "body" => "",
41
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
42
+ "count" => 1,
43
+ "name" => "process_action.hanami",
44
+ "title" => ""
45
+ )
46
+ ]
47
+ )
48
+ end
49
+ end
50
+ end
@@ -115,7 +115,7 @@ if DependencyHelper.rails_present?
115
115
  context "with custom params" do
116
116
  let(:app) do
117
117
  lambda do |env|
118
- env[Appsignal::Rack::APPSIGNAL_TRANSACTION].params = { "custom_param" => "yes" }
118
+ env[Appsignal::Rack::APPSIGNAL_TRANSACTION].set_params("custom_param" => "yes")
119
119
  end
120
120
  end
121
121
 
@@ -328,7 +328,7 @@ describe Appsignal::Transaction do
328
328
 
329
329
  context "with custom params set on transaction" do
330
330
  before do
331
- transaction.params = { :foo => "bar" }
331
+ transaction.set_params(:foo => "bar")
332
332
  end
333
333
 
334
334
  it "returns custom parameters" do
@@ -348,9 +348,92 @@ describe Appsignal::Transaction do
348
348
  end
349
349
 
350
350
  describe "#params=" do
351
+ around { |example| keep_transactions { example.run } }
352
+
351
353
  it "sets params on the transaction" do
352
- transaction.params = { :foo => "bar" }
353
- expect(transaction.params).to eq(:foo => "bar")
354
+ params = { "foo" => "bar" }
355
+ silence { transaction.params = params }
356
+
357
+ transaction.complete # Sample the data
358
+ expect(transaction.params).to eq(params)
359
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
360
+ end
361
+
362
+ it "logs a deprecation warning" do
363
+ transaction.params = { "foo" => "bar" }
364
+
365
+ expect(log_contents(log)).to contains_log(
366
+ :warn,
367
+ "Transaction#params= is deprecated." \
368
+ "Use Transaction#set_params or #set_params_if_nil instead."
369
+ )
370
+ end
371
+ end
372
+
373
+ describe "#set_params" do
374
+ around { |example| keep_transactions { example.run } }
375
+
376
+ context "when the params are set" do
377
+ it "updates the params on the transaction" do
378
+ params = { "key" => "value" }
379
+ transaction.set_params(params)
380
+
381
+ transaction.complete # Sample the data
382
+ expect(transaction.params).to eq(params)
383
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
384
+ end
385
+ end
386
+
387
+ context "when the given params is nil" do
388
+ it "does not update the params on the transaction" do
389
+ params = { "key" => "value" }
390
+ transaction.set_params(params)
391
+ transaction.set_params(nil)
392
+
393
+ transaction.complete # Sample the data
394
+ expect(transaction.params).to eq(params)
395
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
396
+ end
397
+ end
398
+ end
399
+
400
+ describe "#set_params_if_nil" do
401
+ around { |example| keep_transactions { example.run } }
402
+
403
+ context "when the params are not set" do
404
+ it "sets the params on the transaction" do
405
+ params = { "key" => "value" }
406
+ transaction.set_params_if_nil(params)
407
+
408
+ transaction.complete # Sample the data
409
+ expect(transaction.params).to eq(params)
410
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
411
+ end
412
+
413
+ context "when the given params is nil" do
414
+ it "does not update the params on the transaction" do
415
+ params = { "key" => "value" }
416
+ transaction.set_params(params)
417
+ transaction.set_params_if_nil(nil)
418
+
419
+ transaction.complete # Sample the data
420
+ expect(transaction.params).to eq(params)
421
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
422
+ end
423
+ end
424
+ end
425
+
426
+ context "when the params are set" do
427
+ it "does not update the params on the transaction" do
428
+ preset_params = { "other" => "params" }
429
+ params = { "key" => "value" }
430
+ transaction.set_params(preset_params)
431
+ transaction.set_params_if_nil(params)
432
+
433
+ transaction.complete # Sample the data
434
+ expect(transaction.params).to eq(preset_params)
435
+ expect(transaction.to_h.dig("sample_data", "params")).to eq(preset_params)
436
+ end
354
437
  end
355
438
  end
356
439
 
@@ -1158,7 +1241,7 @@ describe Appsignal::Transaction do
1158
1241
 
1159
1242
  context "with custom params" do
1160
1243
  before do
1161
- transaction.params = { :foo => "bar", :baz => :bat }
1244
+ transaction.set_params(:foo => "bar", :baz => :bat)
1162
1245
  end
1163
1246
 
1164
1247
  it "returns custom params" do
@@ -21,11 +21,9 @@ module HanamiApp
21
21
 
22
22
  class Error < Hanami::Action
23
23
  def handle(_request, _response)
24
- raise ExampleError
24
+ raise ExampleException, "exception message"
25
25
  end
26
26
  end
27
27
  end
28
28
  end
29
-
30
- class ExampleError < StandardError; end
31
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.9.2
5
5
  platform: java
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-06-21 00:00:00.000000000 Z
13
+ date: 2024-06-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -288,6 +288,7 @@ files:
288
288
  - lib/appsignal/rack/abstract_middleware.rb
289
289
  - lib/appsignal/rack/event_handler.rb
290
290
  - lib/appsignal/rack/generic_instrumentation.rb
291
+ - lib/appsignal/rack/hanami_middleware.rb
291
292
  - lib/appsignal/rack/rails_instrumentation.rb
292
293
  - lib/appsignal/rack/sinatra_instrumentation.rb
293
294
  - lib/appsignal/rack/streaming_listener.rb
@@ -390,6 +391,7 @@ files:
390
391
  - spec/lib/appsignal/rack/abstract_middleware_spec.rb
391
392
  - spec/lib/appsignal/rack/event_handler_spec.rb
392
393
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
394
+ - spec/lib/appsignal/rack/hanami_middleware_spec.rb
393
395
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb
394
396
  - spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
395
397
  - spec/lib/appsignal/rack/streaming_listener_spec.rb