appsignal 3.9.0-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: 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