appsignal 4.0.0-java → 4.0.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: 8b24fff89ddabacc2b97eedd0f2350e332084c31c4e879dab2b9f13e02f631e2
4
- data.tar.gz: ec09dc858c399b9004cebb9afcdb1e041ba4f0efd068f173c6177e2bb5d10b1c
3
+ metadata.gz: 9a43d932132633873f343b1a180772e9949115f85fcce3d9e13d9af6a23eb824
4
+ data.tar.gz: 2d3d8fa4dfe45a3a42c89955bd243cd2c7871b3f2ac2978e7bef8333b6518b5e
5
5
  SHA512:
6
- metadata.gz: 0ff5785db46ea54a79b993742b1f2730cae2bb236069c331414782732ed47de3a0c28a95e3bb8c9db45adcf2a402083af88f909d5065d839551d9f52bc560e64
7
- data.tar.gz: 6da6a43068a8edf31158269f956be507f2125ec61dd8374192f228582c730862a5a659d5b2f530e7c995264e571d2e27daf049367089608dcdbbb63b5db16ef9
6
+ metadata.gz: a7f97a072db897dc6433d059067ad0bd0db054ec6d2b7a2f6c16ea9e8876e7b7f1b5ec3abdfba554fda60c6a211c8b3455a39221a9c7ebe8beb336a129fa0eec
7
+ data.tar.gz: 52e9a83f45fe345d895453e38833047c305f0b474918d8510148959a87a8e1ae797773213082152a03a19f8fd9cd7adfc37b5b0b6c33572f97fd9fdf3d67b5e1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 4.0.2
4
+
5
+ _Published on 2024-08-23._
6
+
7
+ ### Fixed
8
+
9
+ - Do not log a warning for `nil` data being added as sample data, but silently ignore it because we don't support it. (patch [0a658e5e](https://github.com/appsignal/appsignal-ruby/commit/0a658e5e523f23f87b7d6e0b88bf6d6bea529f06))
10
+ - Fix Rails session data not being reported. (patch [1565c7f0](https://github.com/appsignal/appsignal-ruby/commit/1565c7f0a55e8c2e51b615863b12d13a2b246949))
11
+
12
+ ## 4.0.1
13
+
14
+ _Published on 2024-08-23._
15
+
16
+ ### Fixed
17
+
18
+ - Do not report `Sidekiq::JobRetry::Skip` errors. These errors would be reported by our Rails error subscriber. This is an internal Sidekiq error we do not need to report. (patch [9ea2d3e8](https://github.com/appsignal/appsignal-ruby/commit/9ea2d3e83657d115baf166257a50c7e3394318aa))
19
+ - Do not report `SystemExit` errors from our `at_exit` error reporter. (patch [e9c0cad3](https://github.com/appsignal/appsignal-ruby/commit/e9c0cad3d672e68a63ca9c33cfa30a3434c77d04))
20
+
3
21
  ## 4.0.0
4
22
 
5
23
  _Published on 2024-08-23._
@@ -24,6 +24,7 @@ module Appsignal
24
24
  class AtExitCallback
25
25
  def self.call
26
26
  error = $! # rubocop:disable Style/SpecialGlobalVars
27
+ return if ignored_error?(error)
27
28
  return if Appsignal::Transaction.last_errors.include?(error)
28
29
 
29
30
  Appsignal.report_error(error) do |transaction|
@@ -31,6 +32,15 @@ module Appsignal
31
32
  end
32
33
  Appsignal.stop("at_exit")
33
34
  end
35
+
36
+ IGNORED_ERRORS = [
37
+ # Normal exits from the application we do not need to report
38
+ SystemExit
39
+ ].freeze
40
+
41
+ def self.ignored_error?(error)
42
+ IGNORED_ERRORS.include?(error.class)
43
+ end
34
44
  end
35
45
  end
36
46
  end
@@ -80,6 +80,8 @@ module Appsignal
80
80
  class RailsErrorReporterSubscriber
81
81
  class << self
82
82
  def report(error, handled:, severity:, context: {}, source: nil) # rubocop:disable Lint/UnusedMethodArgument
83
+ return if ignored_error?(error)
84
+
83
85
  is_rails_runner = source == "application.runner.railties"
84
86
  namespace, action_name, tags, custom_data = context_for(context.dup)
85
87
 
@@ -100,6 +102,16 @@ module Appsignal
100
102
 
101
103
  private
102
104
 
105
+ IGNORED_ERRORS = [
106
+ # We don't need to alert Sidekiq job skip errors.
107
+ # This is an internal Sidekiq error.
108
+ "Sidekiq::JobRetry::Skip"
109
+ ].freeze
110
+
111
+ def ignored_error?(error)
112
+ IGNORED_ERRORS.include?(error.class.name)
113
+ end
114
+
103
115
  def context_for(context)
104
116
  tags = {}
105
117
 
@@ -142,9 +142,7 @@ module Appsignal
142
142
  transaction.set_metadata("method", request_method) if request_method
143
143
 
144
144
  transaction.add_params { params_for(request) }
145
- transaction.add_session_data do
146
- request.session if request.respond_to?(:session)
147
- end
145
+ transaction.add_session_data { session_data_for(request) }
148
146
  transaction.add_headers do
149
147
  request.env if request.respond_to?(:env)
150
148
  end
@@ -174,6 +172,18 @@ module Appsignal
174
172
  nil
175
173
  end
176
174
 
175
+ def session_data_for(request)
176
+ return unless request.respond_to?(:session)
177
+
178
+ request.session.to_h
179
+ rescue => error
180
+ Appsignal.internal_logger.error(
181
+ "Exception while fetching session data from '#{@request_class}': " \
182
+ "#{error.class} #{error}"
183
+ )
184
+ nil
185
+ end
186
+
177
187
  def request_for(env)
178
188
  @request_class.new(env)
179
189
  end
@@ -28,7 +28,7 @@ module Appsignal
28
28
  end
29
29
 
30
30
  def value
31
- value = nil
31
+ value = UNSET_VALUE
32
32
  @blocks.map! do |block_or_value|
33
33
  new_value =
34
34
  if block_or_value.respond_to?(:call)
@@ -63,6 +63,8 @@ module Appsignal
63
63
 
64
64
  private
65
65
 
66
+ UNSET_VALUE = nil
67
+
66
68
  # Method called by `dup` and `clone` to create a duplicate instance.
67
69
  # Make sure the `@blocks` variable is also properly duplicated.
68
70
  def initialize_copy(original)
@@ -81,11 +83,13 @@ module Appsignal
81
83
 
82
84
  def merge_values(value_original, value_new)
83
85
  unless value_new.instance_of?(value_original.class)
84
- Appsignal.internal_logger.warn(
85
- "The sample data '#{@key}' changed type from " \
86
- "'#{value_original.class}' to '#{value_new.class}'. " \
87
- "These types can not be merged. Using new '#{value_new.class}' type."
88
- )
86
+ unless value_original == UNSET_VALUE
87
+ Appsignal.internal_logger.warn(
88
+ "The sample data '#{@key}' changed type from " \
89
+ "'#{value_original.class}' to '#{value_new.class}'. " \
90
+ "These types can not be merged. Using new '#{value_new.class}' type."
91
+ )
92
+ end
89
93
  return value_new
90
94
  end
91
95
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "4.0.0"
4
+ VERSION = "4.0.2"
5
5
  end
@@ -59,7 +59,7 @@ describe Appsignal::Hooks::AtExit::AtExitCallback do
59
59
  end
60
60
  end
61
61
 
62
- it "doesn't report the error if is also the last error reported" do
62
+ it "doesn't report the error if it is also the last error reported" do
63
63
  with_error(ExampleException, "error message") do |error|
64
64
  Appsignal.report_error(error)
65
65
  expect(created_transactions.count).to eq(1)
@@ -69,4 +69,15 @@ describe Appsignal::Hooks::AtExit::AtExitCallback do
69
69
  end.to_not change { created_transactions.count }.from(1)
70
70
  end
71
71
  end
72
+
73
+ it "doesn't report the error if it is a SystemExit exception" do
74
+ with_error(SystemExit, "error message") do |error|
75
+ Appsignal.report_error(error)
76
+ expect(created_transactions.count).to eq(1)
77
+
78
+ expect do
79
+ call_callback
80
+ end.to_not change { created_transactions.count }.from(1)
81
+ end
82
+ end
72
83
  end
@@ -17,154 +17,10 @@ describe Appsignal::Hooks::ResqueHook do
17
17
 
18
18
  if DependencyHelper.resque_present?
19
19
  describe "#install" do
20
- def perform_rescue_job(klass, options = {})
21
- payload = { "class" => klass.to_s }.merge(options)
22
- job = ::Resque::Job.new(queue, payload)
23
- keep_transactions { job.perform }
24
- end
25
-
26
- let(:queue) { "default" }
27
- let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
28
- let(:options) { {} }
29
- before do
30
- start_agent(:options => options)
31
-
32
- class ResqueTestJob
33
- def self.perform(*_args)
34
- end
35
- end
36
-
37
- class ResqueErrorTestJob
38
- def self.perform
39
- raise "resque job error"
40
- end
41
- end
42
-
43
- expect(Appsignal).to receive(:stop) # Resque calls stop after every job
44
- end
45
- around do |example|
46
- keep_transactions { example.run }
47
- end
48
- after do
49
- Object.send(:remove_const, :ResqueTestJob)
50
- Object.send(:remove_const, :ResqueErrorTestJob)
51
- end
52
-
53
- it "tracks a transaction on perform" do
54
- perform_rescue_job(ResqueTestJob)
55
-
56
- transaction = last_transaction
57
- expect(transaction).to have_id
58
- expect(transaction).to have_namespace(namespace)
59
- expect(transaction).to have_action("ResqueTestJob#perform")
60
- expect(transaction).to_not have_error
61
- expect(transaction).to_not include_metadata
62
- expect(transaction).to_not include_breadcrumbs
63
- expect(transaction).to include_tags("queue" => queue)
64
- expect(transaction).to include_event("name" => "perform.resque")
65
- end
66
-
67
- context "with error" do
68
- it "tracks the error on the transaction" do
69
- expect do
70
- perform_rescue_job(ResqueErrorTestJob)
71
- end.to raise_error(RuntimeError, "resque job error")
72
-
73
- transaction = last_transaction
74
- expect(transaction).to have_id
75
- expect(transaction).to have_namespace(namespace)
76
- expect(transaction).to have_action("ResqueErrorTestJob#perform")
77
- expect(transaction).to have_error("RuntimeError", "resque job error")
78
- expect(transaction).to_not include_metadata
79
- expect(transaction).to_not include_breadcrumbs
80
- expect(transaction).to include_tags("queue" => queue)
81
- expect(transaction).to include_event("name" => "perform.resque")
82
- end
83
- end
84
-
85
- context "with arguments" do
86
- let(:options) { { :filter_parameters => ["foo"] } }
87
-
88
- it "filters out configured arguments" do
89
- perform_rescue_job(
90
- ResqueTestJob,
91
- "args" => [
92
- "foo",
93
- {
94
- "foo" => "secret",
95
- "bar" => "Bar",
96
- "baz" => { "1" => "foo" }
97
- }
98
- ]
99
- )
100
-
101
- transaction = last_transaction
102
- expect(transaction).to have_id
103
- expect(transaction).to have_namespace(namespace)
104
- expect(transaction).to have_action("ResqueTestJob#perform")
105
- expect(transaction).to_not have_error
106
- expect(transaction).to_not include_metadata
107
- expect(transaction).to_not include_breadcrumbs
108
- expect(transaction).to include_tags("queue" => queue)
109
- expect(transaction).to include_event("name" => "perform.resque")
110
- expect(transaction).to include_params(
111
- [
112
- "foo",
113
- {
114
- "foo" => "[FILTERED]",
115
- "bar" => "Bar",
116
- "baz" => { "1" => "foo" }
117
- }
118
- ]
119
- )
120
- end
121
- end
122
-
123
- context "with active job" do
124
- before do
125
- module ActiveJobMock
126
- module QueueAdapters
127
- module ResqueAdapter
128
- module JobWrapper
129
- class << self
130
- def perform(job_data)
131
- # Basic ActiveJob stub for this test.
132
- # I haven't found a way to run Resque in a testing mode.
133
- Appsignal.set_action(job_data["job_class"])
134
- end
135
- end
136
- end
137
- end
138
- end
139
- end
140
-
141
- stub_const "ActiveJob", ActiveJobMock
142
- end
143
- after { Object.send(:remove_const, :ActiveJobMock) }
144
-
145
- it "does not set arguments but lets the ActiveJob integration handle it" do
146
- perform_rescue_job(
147
- ResqueTestJob,
148
- "class" => "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper",
149
- "args" => [
150
- {
151
- "job_class" => "ResqueTestJobByActiveJob#perform",
152
- "arguments" => ["an argument", "second argument"]
153
- }
154
- ]
155
- )
20
+ before { start_agent }
156
21
 
157
- transaction = last_transaction
158
- expect(transaction).to have_id
159
- expect(transaction).to have_namespace(namespace)
160
- expect(transaction).to have_action("ResqueTestJobByActiveJob#perform")
161
- expect(transaction).to_not have_error
162
- expect(transaction).to_not include_metadata
163
- expect(transaction).to_not include_breadcrumbs
164
- expect(transaction).to include_tags("queue" => queue)
165
- expect(transaction).to include_event("name" => "perform.resque")
166
- expect(transaction).to_not include_params
167
- end
22
+ it "adds the ResqueIntegration module to Resque::Job" do
23
+ expect(Resque::Job.included_modules).to include(Appsignal::Integrations::ResqueIntegration)
168
24
  end
169
25
  end
170
26
  end
@@ -229,6 +229,17 @@ if DependencyHelper.rails_present?
229
229
  expect(last_transaction).to have_error("ExampleStandardError", "error message")
230
230
  end
231
231
 
232
+ it "ignores Sidekiq::JobRetry::Skip errors" do
233
+ require "sidekiq"
234
+ require "sidekiq/job_retry"
235
+
236
+ with_rails_error_reporter do
237
+ Rails.error.handle { raise Sidekiq::JobRetry::Skip, "error message" }
238
+ end
239
+
240
+ expect(last_transaction).to_not have_error
241
+ end
242
+
232
243
  context "when no transaction is active" do
233
244
  it "reports the error on a new transaction" do
234
245
  with_rails_error_reporter do
@@ -0,0 +1,155 @@
1
+ require "appsignal/integrations/resque"
2
+
3
+ if DependencyHelper.resque_present?
4
+ describe Appsignal::Integrations::ResqueIntegration do
5
+ def perform_rescue_job(klass, options = {})
6
+ payload = { "class" => klass.to_s }.merge(options)
7
+ job = ::Resque::Job.new(queue, payload)
8
+ keep_transactions { job.perform }
9
+ end
10
+
11
+ let(:queue) { "default" }
12
+ let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
13
+ let(:options) { {} }
14
+ before do
15
+ start_agent(:options => options)
16
+
17
+ class ResqueTestJob
18
+ def self.perform(*_args)
19
+ end
20
+ end
21
+
22
+ class ResqueErrorTestJob
23
+ def self.perform
24
+ raise "resque job error"
25
+ end
26
+ end
27
+
28
+ expect(Appsignal).to receive(:stop) # Resque calls stop after every job
29
+ end
30
+ around do |example|
31
+ keep_transactions { example.run }
32
+ end
33
+ after do
34
+ Object.send(:remove_const, :ResqueTestJob)
35
+ Object.send(:remove_const, :ResqueErrorTestJob)
36
+ end
37
+
38
+ it "tracks a transaction on perform" do
39
+ perform_rescue_job(ResqueTestJob)
40
+
41
+ transaction = last_transaction
42
+ expect(transaction).to have_id
43
+ expect(transaction).to have_namespace(namespace)
44
+ expect(transaction).to have_action("ResqueTestJob#perform")
45
+ expect(transaction).to_not have_error
46
+ expect(transaction).to_not include_metadata
47
+ expect(transaction).to_not include_breadcrumbs
48
+ expect(transaction).to include_tags("queue" => queue)
49
+ expect(transaction).to include_event("name" => "perform.resque")
50
+ end
51
+
52
+ context "with error" do
53
+ it "tracks the error on the transaction" do
54
+ expect do
55
+ perform_rescue_job(ResqueErrorTestJob)
56
+ end.to raise_error(RuntimeError, "resque job error")
57
+
58
+ transaction = last_transaction
59
+ expect(transaction).to have_id
60
+ expect(transaction).to have_namespace(namespace)
61
+ expect(transaction).to have_action("ResqueErrorTestJob#perform")
62
+ expect(transaction).to have_error("RuntimeError", "resque job error")
63
+ expect(transaction).to_not include_metadata
64
+ expect(transaction).to_not include_breadcrumbs
65
+ expect(transaction).to include_tags("queue" => queue)
66
+ expect(transaction).to include_event("name" => "perform.resque")
67
+ end
68
+ end
69
+
70
+ context "with arguments" do
71
+ let(:options) { { :filter_parameters => ["foo"] } }
72
+
73
+ it "filters out configured arguments" do
74
+ perform_rescue_job(
75
+ ResqueTestJob,
76
+ "args" => [
77
+ "foo",
78
+ {
79
+ "foo" => "secret",
80
+ "bar" => "Bar",
81
+ "baz" => { "1" => "foo" }
82
+ }
83
+ ]
84
+ )
85
+
86
+ transaction = last_transaction
87
+ expect(transaction).to have_id
88
+ expect(transaction).to have_namespace(namespace)
89
+ expect(transaction).to have_action("ResqueTestJob#perform")
90
+ expect(transaction).to_not have_error
91
+ expect(transaction).to_not include_metadata
92
+ expect(transaction).to_not include_breadcrumbs
93
+ expect(transaction).to include_tags("queue" => queue)
94
+ expect(transaction).to include_event("name" => "perform.resque")
95
+ expect(transaction).to include_params(
96
+ [
97
+ "foo",
98
+ {
99
+ "foo" => "[FILTERED]",
100
+ "bar" => "Bar",
101
+ "baz" => { "1" => "foo" }
102
+ }
103
+ ]
104
+ )
105
+ end
106
+ end
107
+
108
+ context "with active job" do
109
+ before do
110
+ module ActiveJobMock
111
+ module QueueAdapters
112
+ module ResqueAdapter
113
+ module JobWrapper
114
+ class << self
115
+ def perform(job_data)
116
+ # Basic ActiveJob stub for this test.
117
+ # I haven't found a way to run Resque in a testing mode.
118
+ Appsignal.set_action(job_data["job_class"])
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ stub_const "ActiveJob", ActiveJobMock
127
+ end
128
+ after { Object.send(:remove_const, :ActiveJobMock) }
129
+
130
+ it "does not set arguments but lets the ActiveJob integration handle it" do
131
+ perform_rescue_job(
132
+ ResqueTestJob,
133
+ "class" => "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper",
134
+ "args" => [
135
+ {
136
+ "job_class" => "ResqueTestJobByActiveJob#perform",
137
+ "arguments" => ["an argument", "second argument"]
138
+ }
139
+ ]
140
+ )
141
+
142
+ transaction = last_transaction
143
+ expect(transaction).to have_id
144
+ expect(transaction).to have_namespace(namespace)
145
+ expect(transaction).to have_action("ResqueTestJobByActiveJob#perform")
146
+ expect(transaction).to_not have_error
147
+ expect(transaction).to_not include_metadata
148
+ expect(transaction).to_not include_breadcrumbs
149
+ expect(transaction).to include_tags("queue" => queue)
150
+ expect(transaction).to include_event("name" => "perform.resque")
151
+ expect(transaction).to_not include_params
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,4 +1,14 @@
1
1
  describe Appsignal::Rack::AbstractMiddleware do
2
+ class HashLike < Hash
3
+ def initialize(value)
4
+ @value = value
5
+ end
6
+
7
+ def to_h
8
+ @value
9
+ end
10
+ end
11
+
2
12
  let(:app) { DummyApp.new }
3
13
  let(:request_path) { "/some/path" }
4
14
  let(:env) do
@@ -261,6 +271,13 @@ describe Appsignal::Rack::AbstractMiddleware do
261
271
 
262
272
  expect(last_transaction).to include_session_data("session" => "data", "user_id" => 123)
263
273
  end
274
+
275
+ it "sets session data if the session is a Hash-like type" do
276
+ env["rack.session"] = HashLike.new("hash-like" => "value", "user_id" => 123)
277
+ make_request
278
+
279
+ expect(last_transaction).to include_session_data("hash-like" => "value", "user_id" => 123)
280
+ end
264
281
  end
265
282
 
266
283
  context "with queue start header" do
@@ -3,9 +3,17 @@ describe Appsignal::SampleData do
3
3
 
4
4
  describe "#add" do
5
5
  it "sets the given value" do
6
- data.add(:key1 => "value 1")
6
+ logs =
7
+ capture_logs do
8
+ data.add(:key1 => "value 1")
9
+ end
7
10
 
8
11
  expect(data.value).to eq(:key1 => "value 1")
12
+
13
+ expect(logs).to_not contains_log(
14
+ :error,
15
+ "Sample data 'data_key': Unsupported data type 'NilClass' received: nil"
16
+ )
9
17
  end
10
18
 
11
19
  it "adds the given value with the block being leading" do
@@ -14,6 +22,23 @@ describe Appsignal::SampleData do
14
22
  expect(data.value).to eq(:key2 => "value 2")
15
23
  end
16
24
 
25
+ it "doesn't add nil to the data" do
26
+ logs =
27
+ capture_logs do
28
+ data.add([1])
29
+ data.add(nil)
30
+ data.add { nil }
31
+ data.add([2, 3])
32
+ end
33
+
34
+ expect(data.value).to eq([1, 2, 3])
35
+ expect(logs).to contains_log(
36
+ :error,
37
+ "Sample data 'data_key': Unsupported data type 'NilClass' received: nil"
38
+ )
39
+ expect(logs).to_not contains_log(:warn, "The sample data 'data_key' changed type")
40
+ end
41
+
17
42
  it "merges multiple values" do
18
43
  data.add(:key1 => "value 1")
19
44
  data.add(:key2 => "value 2")
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: 4.0.0
4
+ version: 4.0.2
5
5
  platform: java
6
6
  authors:
7
7
  - Robert Beekman
@@ -386,6 +386,7 @@ files:
386
386
  - spec/lib/appsignal/integrations/object_spec.rb
387
387
  - spec/lib/appsignal/integrations/que_spec.rb
388
388
  - spec/lib/appsignal/integrations/railtie_spec.rb
389
+ - spec/lib/appsignal/integrations/resque_spec.rb
389
390
  - spec/lib/appsignal/integrations/shoryuken_spec.rb
390
391
  - spec/lib/appsignal/integrations/sidekiq_spec.rb
391
392
  - spec/lib/appsignal/integrations/webmachine_spec.rb