appsignal 3.9.1-java → 3.9.2-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41bb798be594cced035267d2df31d1055dedf7fa67ec22b3807c27833f828ed4
4
- data.tar.gz: 69deaf2c8fe7b5abf5959a176645e640d72e9b75fb346eeba8ca9a53449374ed
3
+ metadata.gz: d8242c596a38a842614a47cb94b88a666de659fad2b0c5270dc94747c0ec996c
4
+ data.tar.gz: e604b5e5acd710f2978579db85b3e274461d4c2c3b68065c9e4541b2a7f4b133
5
5
  SHA512:
6
- metadata.gz: 71e4034081ed264899515c1de46b3e1d48d24dfc43e8b0861ddadb92bfbf803124491046f8ff6fbeeaa7b4a38e55e4638e822b39744ba6a7b5de986182d3f6b4
7
- data.tar.gz: 3aa39a4f0f3ed36782ffbc3501eedfb6d811248abcf4e304be4d41f64ec24a4f938c8fe130bfb7de85751780d063783a72a1a389717a636fadc71e042305687f
6
+ metadata.gz: 682a946ea99d3db5d552ad1c6afa1ed67493c71427dfd55172a03349f6dfdbbaaabf6459123c19598e6c66164293cd845431264e7b6d832b5cd40b870c642f67
7
+ data.tar.gz: ce4f0c6fdd313528c2b91ad3b0c4c50f3e05bb93b56d2f2a983da830ff4a0dfa8a1184ce6b8cad0c20091b7e1d07dedea4f259f8e528ecc86295b8ea3a375e0b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
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
+
3
23
  ## 3.9.1
4
24
 
5
25
  _Published on 2024-06-24._
@@ -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
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.9.1"
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
@@ -352,7 +352,7 @@ describe Appsignal::Transaction do
352
352
 
353
353
  it "sets params on the transaction" do
354
354
  params = { "foo" => "bar" }
355
- transaction.params = params
355
+ silence { transaction.params = params }
356
356
 
357
357
  transaction.complete # Sample the data
358
358
  expect(transaction.params).to eq(params)
@@ -1241,7 +1241,7 @@ describe Appsignal::Transaction do
1241
1241
 
1242
1242
  context "with custom params" do
1243
1243
  before do
1244
- transaction.params = { :foo => "bar", :baz => :bat }
1244
+ transaction.set_params(:foo => "bar", :baz => :bat)
1245
1245
  end
1246
1246
 
1247
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.1
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-24 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