appsignal 3.4.4-java → 3.4.6-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: d424b2fb153945a0ead961e5c02ec4ee58bababe8135403c49c990ba2f2eb1f1
4
- data.tar.gz: b633f608930f213d0b83e0903334e96a17fa19fa2c7aff6d2485db1b9aa0d648
3
+ metadata.gz: af3d35d071f1564b0cbdaf41dbc8d28578aa4bef90ac880a3731b227fb067643
4
+ data.tar.gz: 69d319fa4870d64c3e54bb78f12c643934bb0a17c20145dcc373b52defcea554
5
5
  SHA512:
6
- metadata.gz: a313ef38d4cee93e9c0b8dfe503d2bf4dae2410b69fd8d6f78210fe1779d7d9644bc973ebcc000ae61691faacd89c56853862b8b1970dc6c8d69026ed8263f55
7
- data.tar.gz: 87aac29579b855242d002fd12645716879af8eb4c8ec2c4afe79227a3225923b9e44a5a85f701add17bcdba74a6fadadc78b0f501ec9f42e4acbd1ae936ee51a
6
+ metadata.gz: a977d7886759656795f443e7488420708c54e1086701f55a3d17aec8f22c1a50a54068b2a63b0e804433913f79aee7927e02bd97890cb055ef0abd7015ece32f
7
+ data.tar.gz: e23e971908c575908983bd7f16ed6321f559799af5e6442766208532a117722dc51d245b11d723a08a22e348b18cbec6f2911f52abe598df87be24cc2aea1743
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.4.6
4
+
5
+ ### Changed
6
+
7
+ - [85c155a0](https://github.com/appsignal/appsignal-ruby/commit/85c155a0a4b2b618c04db52c34ee7f0adba8f3c5) patch - When sanitizing an array or hash, replace recursively nested values with a placeholder string. This fixes a SystemStackError issue when sanitising arrays and hashes.
8
+
9
+ ## 3.4.5
10
+
11
+ ### Added
12
+
13
+ - [e5e79d9a](https://github.com/appsignal/appsignal-ruby/commit/e5e79d9aa17006a6995e9ea18fabdc14a2356c82) patch - Add `filter_metadata` config option to filter metadata set on Transactions set by default. Metadata like `path`, (request) `method`, `request_id`, `hostname`, etc. This can be useful if there's PII or other sensitive data in any of the app's metadata.
14
+
15
+ ### Fixed
16
+
17
+ - [5a4797c8](https://github.com/appsignal/appsignal-ruby/commit/5a4797c8560c2d1e60b4f1a750136c906505746c) patch - Fix Sinatra request custom request parameters method. If the Sinatra option `params_method` is set, a different method than `params` will be called on the request object to fetch the request parameters. This can be used to add custom filtering to parameters recorded by AppSignal.
18
+ - [9cdee8aa](https://github.com/appsignal/appsignal-ruby/commit/9cdee8aae3cb7b969583493440469ac0dfea764f) patch - Log error when the argument type of the breadcrumb metadata is invalid. This metadata argument should be a Hash, and other values are not supported. More information can be found in the [Ruby gem breadcrumb documentation](https://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html).
19
+
20
+ ```ruby
21
+ Appsignal.add_breadcrumb(
22
+ "breadcrumb category",
23
+ "breadcrumb action",
24
+ "some message",
25
+ { :metadata_key => "some value" } # This needs to be a Hash object
26
+ )
27
+ ```
28
+
3
29
  ## 3.4.4
4
30
 
5
31
  ### Fixed
@@ -24,6 +24,7 @@ module Appsignal
24
24
  :enable_rails_error_reporter => true,
25
25
  :endpoint => "https://push.appsignal.com",
26
26
  :files_world_accessible => true,
27
+ :filter_metadata => [],
27
28
  :filter_parameters => [],
28
29
  :filter_session_data => [],
29
30
  :ignore_actions => [],
@@ -77,6 +78,7 @@ module Appsignal
77
78
  "APPSIGNAL_ENABLE_GVL_WAITING_THREADS" => :enable_gvl_waiting_threads,
78
79
  "APPSIGNAL_ENABLE_RAILS_ERROR_REPORTER" => :enable_rails_error_reporter,
79
80
  "APPSIGNAL_FILES_WORLD_ACCESSIBLE" => :files_world_accessible,
81
+ "APPSIGNAL_FILTER_METADATA" => :filter_metadata,
80
82
  "APPSIGNAL_FILTER_PARAMETERS" => :filter_parameters,
81
83
  "APPSIGNAL_FILTER_SESSION_DATA" => :filter_session_data,
82
84
  "APPSIGNAL_HOSTNAME" => :hostname,
@@ -150,6 +152,7 @@ module Appsignal
150
152
  # @api private
151
153
  ENV_ARRAY_KEYS = %w[
152
154
  APPSIGNAL_DNS_SERVERS
155
+ APPSIGNAL_FILTER_METADATA
153
156
  APPSIGNAL_FILTER_PARAMETERS
154
157
  APPSIGNAL_FILTER_SESSION_DATA
155
158
  APPSIGNAL_IGNORE_ACTIONS
@@ -47,13 +47,14 @@ module Appsignal
47
47
  end
48
48
 
49
49
  def call_with_appsignal_monitoring(env)
50
- env[:params_method] = @options[:params_method] if @options[:params_method]
50
+ options = { :force => @options.include?(:force) && @options[:force] }
51
+ options.merge!(:params_method => @options[:params_method]) if @options[:params_method]
51
52
  request = @options.fetch(:request_class, Sinatra::Request).new(env)
52
53
  transaction = Appsignal::Transaction.create(
53
54
  SecureRandom.uuid,
54
55
  Appsignal::Transaction::HTTP_REQUEST,
55
56
  request,
56
- :force => @options.include?(:force) && @options[:force]
57
+ options
57
58
  )
58
59
  begin
59
60
  Appsignal.instrument("process_action.sinatra") do
@@ -186,6 +186,12 @@ module Appsignal
186
186
  # @see https://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html
187
187
  # Breadcrumb reference
188
188
  def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
189
+ unless metadata.is_a? Hash
190
+ Appsignal.logger.error "add_breadcrumb: Cannot add breadcrumb. " \
191
+ "The given metadata argument is not a Hash."
192
+ return
193
+ end
194
+
189
195
  @breadcrumbs.push(
190
196
  :time => time.to_i,
191
197
  :category => category,
@@ -308,6 +314,7 @@ module Appsignal
308
314
 
309
315
  def set_metadata(key, value)
310
316
  return unless key && value
317
+ return if Appsignal.config[:filter_metadata].include?(key.to_s)
311
318
 
312
319
  @ext.set_metadata(key, value)
313
320
  end
@@ -337,7 +344,7 @@ module Appsignal
337
344
  :params => sanitized_params,
338
345
  :environment => sanitized_environment,
339
346
  :session_data => sanitized_session_data,
340
- :metadata => metadata,
347
+ :metadata => sanitized_metadata,
341
348
  :tags => sanitized_tags,
342
349
  :breadcrumbs => breadcrumbs
343
350
  }.each do |key, data|
@@ -522,12 +529,17 @@ module Appsignal
522
529
  )
523
530
  end
524
531
 
525
- # Returns metadata from the environment.
532
+ # Returns sanitized metadata set by {#set_metadata} and from the
533
+ # {#environment}.
526
534
  #
527
- # @return [nil] if no `:metadata` key is present in the {#environment}.
528
535
  # @return [Hash<String, Object>]
529
- def metadata
530
- environment[:metadata]
536
+ def sanitized_metadata
537
+ metadata = environment[:metadata]
538
+ return unless metadata
539
+
540
+ metadata
541
+ .transform_keys(&:to_s)
542
+ .except(*Appsignal.config[:filter_metadata])
531
543
  end
532
544
 
533
545
  # Returns the environment for a transaction.
@@ -5,20 +5,21 @@ module Appsignal
5
5
  # @api private
6
6
  class HashSanitizer
7
7
  FILTERED = "[FILTERED]"
8
+ RECURSIVE = "[RECURSIVE VALUE]"
8
9
 
9
10
  class << self
10
11
  def sanitize(value, filter_keys = [])
11
- sanitize_value(value, filter_keys)
12
+ sanitize_value(value, filter_keys, [])
12
13
  end
13
14
 
14
15
  private
15
16
 
16
- def sanitize_value(value, filter_keys)
17
+ def sanitize_value(value, filter_keys, seen)
17
18
  case value
18
19
  when Hash
19
- sanitize_hash(value, filter_keys)
20
+ sanitize_hash(value, filter_keys, seen)
20
21
  when Array
21
- sanitize_array(value, filter_keys)
22
+ sanitize_array(value, filter_keys, seen)
22
23
  when TrueClass, FalseClass, NilClass, Integer, String, Symbol, Float
23
24
  unmodified(value)
24
25
  else
@@ -26,23 +27,34 @@ module Appsignal
26
27
  end
27
28
  end
28
29
 
29
- def sanitize_hash(source, filter_keys)
30
+ def sanitize_hash(source, filter_keys, seen)
31
+ seen = seen.clone << source.object_id
32
+
30
33
  {}.tap do |hash|
31
34
  source.each_pair do |key, value|
32
35
  hash[key] =
33
- if filter_keys.include?(key.to_s)
36
+ if seen.include?(value.object_id)
37
+ RECURSIVE
38
+ elsif filter_keys.include?(key.to_s)
34
39
  FILTERED
35
40
  else
36
- sanitize_value(value, filter_keys)
41
+ sanitize_value(value, filter_keys, seen)
37
42
  end
38
43
  end
39
44
  end
40
45
  end
41
46
 
42
- def sanitize_array(source, filter_keys)
47
+ def sanitize_array(source, filter_keys, seen)
48
+ seen = seen.clone << source.object_id
49
+
43
50
  [].tap do |array|
44
51
  source.each_with_index do |item, index|
45
- array[index] = sanitize_value(item, filter_keys)
52
+ array[index] =
53
+ if seen.include?(item.object_id)
54
+ RECURSIVE
55
+ else
56
+ sanitize_value(item, filter_keys, seen)
57
+ end
46
58
  end
47
59
  end
48
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.4.4"
4
+ VERSION = "3.4.6"
5
5
  end
@@ -165,6 +165,7 @@ describe Appsignal::Config do
165
165
  :enable_rails_error_reporter => true,
166
166
  :endpoint => "https://push.appsignal.com",
167
167
  :files_world_accessible => true,
168
+ :filter_metadata => [],
168
169
  :filter_parameters => [],
169
170
  :filter_session_data => [],
170
171
  :ignore_actions => [],
@@ -1,39 +1,49 @@
1
1
  if DependencyHelper.sinatra_present?
2
2
  require "appsignal/integrations/sinatra"
3
3
 
4
+ module SinatraRequestHelpers
5
+ def make_request(env)
6
+ middleware.call(env)
7
+ end
8
+
9
+ def make_request_with_error(env, error)
10
+ expect { middleware.call(env) }.to raise_error(error)
11
+ end
12
+ end
13
+
4
14
  describe Appsignal::Rack::SinatraInstrumentation do
15
+ include SinatraRequestHelpers
16
+
5
17
  let(:settings) { double(:raise_errors => false) }
6
18
  let(:app) { double(:call => true, :settings => settings) }
7
19
  let(:env) { { "sinatra.route" => "GET /", :path => "/", :method => "GET" } }
8
20
  let(:middleware) { Appsignal::Rack::SinatraInstrumentation.new(app) }
9
21
 
22
+ before(:context) { start_agent }
23
+ around do |example|
24
+ keep_transactions { example.run }
25
+ end
26
+
10
27
  describe "#call" do
11
- before do
12
- start_agent
13
- allow(middleware).to receive(:raw_payload).and_return({})
14
- allow(Appsignal).to receive(:active?).and_return(true)
15
- end
28
+ before { allow(middleware).to receive(:raw_payload).and_return({}) }
16
29
 
17
- it "should call without monitoring" do
18
- expect(Appsignal::Transaction).to_not receive(:create)
30
+ it "doesn't instrument requests" do
31
+ make_request(env)
32
+ expect(created_transactions.count).to eq(0)
19
33
  end
20
-
21
- after { middleware.call(env) }
22
34
  end
23
35
 
24
36
  describe ".settings" do
25
37
  subject { middleware.settings }
26
38
 
27
- it "should return the app's settings" do
39
+ it "returns the app's settings" do
28
40
  expect(subject).to eq(app.settings)
29
41
  end
30
42
  end
31
43
  end
32
44
 
33
45
  describe Appsignal::Rack::SinatraBaseInstrumentation do
34
- before :context do
35
- start_agent
36
- end
46
+ include SinatraRequestHelpers
37
47
 
38
48
  let(:settings) { double(:raise_errors => false) }
39
49
  let(:app) { double(:call => true, :settings => settings) }
@@ -41,11 +51,16 @@ if DependencyHelper.sinatra_present?
41
51
  let(:options) { {} }
42
52
  let(:middleware) { Appsignal::Rack::SinatraBaseInstrumentation.new(app, options) }
43
53
 
54
+ before(:context) { start_agent }
55
+ around do |example|
56
+ keep_transactions { example.run }
57
+ end
58
+
44
59
  describe "#initialize" do
45
60
  context "with no settings method in the Sinatra app" do
46
61
  let(:app) { double(:call => true) }
47
62
 
48
- it "should not raise errors" do
63
+ it "does not raise errors" do
49
64
  expect(middleware.raise_errors_on).to be(false)
50
65
  end
51
66
  end
@@ -53,7 +68,7 @@ if DependencyHelper.sinatra_present?
53
68
  context "with no raise_errors setting in the Sinatra app" do
54
69
  let(:app) { double(:call => true, :settings => double) }
55
70
 
56
- it "should not raise errors" do
71
+ it "does not raise errors" do
57
72
  expect(middleware.raise_errors_on).to be(false)
58
73
  end
59
74
  end
@@ -61,7 +76,7 @@ if DependencyHelper.sinatra_present?
61
76
  context "with raise_errors turned off in the Sinatra app" do
62
77
  let(:app) { double(:call => true, :settings => double(:raise_errors => false)) }
63
78
 
64
- it "should raise errors" do
79
+ it "raises errors" do
65
80
  expect(middleware.raise_errors_on).to be(false)
66
81
  end
67
82
  end
@@ -69,21 +84,17 @@ if DependencyHelper.sinatra_present?
69
84
  context "with raise_errors turned on in the Sinatra app" do
70
85
  let(:app) { double(:call => true, :settings => double(:raise_errors => true)) }
71
86
 
72
- it "should raise errors" do
87
+ it "raises errors" do
73
88
  expect(middleware.raise_errors_on).to be(true)
74
89
  end
75
90
  end
76
91
  end
77
92
 
78
93
  describe "#call" do
79
- before do
80
- allow(middleware).to receive(:raw_payload).and_return({})
81
- end
94
+ before { allow(middleware).to receive(:raw_payload).and_return({}) }
82
95
 
83
96
  context "when appsignal is active" do
84
- before { allow(Appsignal).to receive(:active?).and_return(true) }
85
-
86
- it "should call with monitoring" do
97
+ it "instruments requests" do
87
98
  expect(middleware).to receive(:call_with_appsignal_monitoring).with(env)
88
99
  end
89
100
  end
@@ -91,34 +102,36 @@ if DependencyHelper.sinatra_present?
91
102
  context "when appsignal is not active" do
92
103
  before { allow(Appsignal).to receive(:active?).and_return(false) }
93
104
 
94
- it "should not call with monitoring" do
95
- expect(middleware).to_not receive(:call_with_appsignal_monitoring)
105
+ it "does not instrument requests" do
106
+ expect(created_transactions.count).to eq(0)
96
107
  end
97
108
 
98
- it "should call the stack" do
109
+ it "calls the next middleware in the stack" do
99
110
  expect(app).to receive(:call).with(env)
100
111
  end
101
112
  end
102
113
 
103
- after { middleware.call(env) }
114
+ after { make_request(env) }
104
115
  end
105
116
 
106
- describe "#call_with_appsignal_monitoring", :error => false do
107
- it "should create a transaction" do
108
- expect(Appsignal::Transaction).to receive(:create).with(
109
- kind_of(String),
110
- Appsignal::Transaction::HTTP_REQUEST,
111
- kind_of(Sinatra::Request),
112
- kind_of(Hash)
113
- ).and_return(double(:set_action_if_nil => nil, :set_http_or_background_queue_start => nil,
114
- :set_metadata => nil))
115
- end
117
+ describe "#call_with_appsignal_monitoring" do
118
+ context "without an error" do
119
+ it "creates a transaction" do
120
+ expect(app).to receive(:call).with(env)
121
+
122
+ make_request(env)
116
123
 
117
- it "should call the app" do
118
- expect(app).to receive(:call).with(env)
124
+ expect(created_transactions.count).to eq(1)
125
+ expect(last_transaction.to_h).to include(
126
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST,
127
+ "action" => "GET /",
128
+ "error" => nil,
129
+ "metadata" => { "path" => "" }
130
+ )
131
+ end
119
132
  end
120
133
 
121
- context "with an error", :error => true do
134
+ context "with an error" do
122
135
  let(:error) { ExampleException }
123
136
  let(:app) do
124
137
  double.tap do |d|
@@ -128,23 +141,48 @@ if DependencyHelper.sinatra_present?
128
141
  end
129
142
 
130
143
  it "records the exception" do
131
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
144
+ make_request_with_error(env, error)
145
+
146
+ expect(created_transactions.count).to eq(1)
147
+ expect(last_transaction.to_h).to include(
148
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST,
149
+ "action" => "GET /",
150
+ "error" => hash_including(
151
+ "name" => "ExampleException",
152
+ "message" => "ExampleException"
153
+ )
154
+ )
132
155
  end
133
156
  end
134
157
 
135
158
  context "with an error in sinatra.error" do
136
- let(:error) { ExampleException }
159
+ let(:error) { ExampleException.new }
137
160
  let(:env) { { "sinatra.error" => error } }
138
161
 
139
- it "records the exception" do
140
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
162
+ context "when raise_errors is off" do
163
+ let(:settings) { double(:raise_errors => false) }
164
+
165
+ it "record the error" do
166
+ make_request(env)
167
+
168
+ expect(created_transactions.count).to eq(1)
169
+ expect(last_transaction.to_h).to include(
170
+ "error" => hash_including(
171
+ "name" => "ExampleException",
172
+ "message" => "ExampleException"
173
+ )
174
+ )
175
+ end
141
176
  end
142
177
 
143
178
  context "when raise_errors is on" do
144
179
  let(:settings) { double(:raise_errors => true) }
145
180
 
146
181
  it "does not record the error" do
147
- expect_any_instance_of(Appsignal::Transaction).to_not receive(:set_error)
182
+ make_request(env)
183
+
184
+ expect(created_transactions.count).to eq(1)
185
+ expect(last_transaction.to_h).to include("error" => nil)
148
186
  end
149
187
  end
150
188
 
@@ -152,22 +190,30 @@ if DependencyHelper.sinatra_present?
152
190
  let(:env) { { "sinatra.error" => error, "sinatra.skip_appsignal_error" => true } }
153
191
 
154
192
  it "does not record the error" do
155
- expect_any_instance_of(Appsignal::Transaction).to_not receive(:set_error)
193
+ make_request(env)
194
+
195
+ expect(created_transactions.count).to eq(1)
196
+ expect(last_transaction.to_h).to include("error" => nil)
156
197
  end
157
198
  end
158
199
  end
159
200
 
160
201
  describe "action name" do
161
- it "should set the action" do
162
- expect_any_instance_of(Appsignal::Transaction)
163
- .to receive(:set_action_if_nil).with("GET /")
202
+ it "sets the action" do
203
+ make_request(env)
204
+
205
+ expect(created_transactions.count).to eq(1)
206
+ expect(last_transaction.to_h).to include("action" => "GET /")
164
207
  end
165
208
 
166
209
  context "without 'sinatra.route' env" do
167
210
  let(:env) { { :path => "/", :method => "GET" } }
168
211
 
169
- it "returns nil" do
170
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil).with(nil)
212
+ it "doesn't set an action name" do
213
+ make_request(env)
214
+
215
+ expect(created_transactions.count).to eq(1)
216
+ expect(last_transaction.to_h).to include("action" => nil)
171
217
  end
172
218
  end
173
219
 
@@ -175,44 +221,90 @@ if DependencyHelper.sinatra_present?
175
221
  before { env["SCRIPT_NAME"] = "/api" }
176
222
 
177
223
  it "should call set_action with an application prefix path" do
178
- expect_any_instance_of(Appsignal::Transaction)
179
- .to receive(:set_action_if_nil).with("GET /api/")
224
+ make_request(env)
225
+
226
+ expect(created_transactions.count).to eq(1)
227
+ expect(last_transaction.to_h).to include("action" => "GET /api/")
180
228
  end
181
229
 
182
230
  context "without 'sinatra.route' env" do
183
231
  let(:env) { { :path => "/", :method => "GET" } }
184
232
 
185
- it "returns nil" do
186
- expect_any_instance_of(Appsignal::Transaction)
187
- .to receive(:set_action_if_nil).with(nil)
233
+ it "doesn't set an action name" do
234
+ make_request(env)
235
+
236
+ expect(created_transactions.count).to eq(1)
237
+ expect(last_transaction.to_h).to include("action" => nil)
188
238
  end
189
239
  end
190
240
  end
191
241
  end
192
242
 
193
- it "should set metadata" do
194
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).twice
243
+ context "metadata" do
244
+ let(:env) { { "PATH_INFO" => "/some/path", "REQUEST_METHOD" => "GET" } }
245
+
246
+ it "sets metadata from the environment" do
247
+ make_request(env)
248
+
249
+ expect(created_transactions.count).to eq(1)
250
+ expect(last_transaction.to_h).to include(
251
+ "metadata" => {
252
+ "method" => "GET",
253
+ "path" => "/some/path"
254
+ },
255
+ "sample_data" => hash_including(
256
+ "environment" => hash_including(
257
+ "REQUEST_METHOD" => "GET",
258
+ "PATH_INFO" => "/some/path"
259
+ )
260
+ )
261
+ )
262
+ end
263
+ end
264
+
265
+ context "with queue start" do
266
+ let(:queue_start_time) { fixed_time * 1_000 }
267
+ let(:env) do
268
+ { "HTTP_X_REQUEST_START" => "t=#{queue_start_time.to_i}" } # in milliseconds
269
+ end
270
+
271
+ it "sets the queue start" do
272
+ make_request(env)
273
+ expect(last_transaction.ext.queue_start).to eq(queue_start_time)
274
+ end
195
275
  end
196
276
 
197
- it "should set the queue start" do
198
- expect_any_instance_of(Appsignal::Transaction)
199
- .to receive(:set_http_or_background_queue_start)
277
+ class FilteredRequest
278
+ def initialize(_args) # rubocop:disable Style/RedundantInitialize
279
+ end
280
+
281
+ def path
282
+ "/static/path"
283
+ end
284
+
285
+ def request_method
286
+ "GET"
287
+ end
288
+
289
+ def filtered_params
290
+ { "abc" => "123" }
291
+ end
200
292
  end
201
293
 
202
294
  context "with overridden request class and params method" do
203
- let(:options) { { :request_class => ::Rack::Request, :params_method => :filtered_params } }
295
+ let(:options) { { :request_class => FilteredRequest, :params_method => :filtered_params } }
204
296
 
205
- it "should use the overridden request class and params method" do
206
- request = ::Rack::Request.new(env)
207
- expect(::Rack::Request).to receive(:new)
208
- .with(env.merge(:params_method => :filtered_params))
209
- .at_least(:once)
210
- .and_return(request)
297
+ it "uses the overridden request class and params method to fetch params" do
298
+ make_request(env)
299
+
300
+ expect(created_transactions.count).to eq(1)
301
+ expect(last_transaction.to_h).to include(
302
+ "sample_data" => hash_including(
303
+ "params" => { "abc" => "123" }
304
+ )
305
+ )
211
306
  end
212
307
  end
213
-
214
- after(:error => false) { middleware.call(env) }
215
- after(:error => true) { expect { middleware.call(env) }.to raise_error(error) }
216
308
  end
217
309
  end
218
310
  end
@@ -1,6 +1,7 @@
1
1
  require "appsignal/rack/streaming_listener"
2
2
 
3
3
  describe Appsignal::Rack::StreamingListener do
4
+ before(:context) { start_agent }
4
5
  let(:headers) { {} }
5
6
  let(:env) do
6
7
  {
@@ -432,6 +432,19 @@ describe Appsignal::Transaction do
432
432
  expect(breadcrumb["metadata"]).to eq({})
433
433
  end
434
434
  end
435
+
436
+ context "with metadata argument that's not a Hash" do
437
+ it "does not add the breadcrumb and logs and error" do
438
+ transaction.add_breadcrumb("category", "action", "message", "invalid metadata")
439
+ transaction.sample_data
440
+
441
+ expect(transaction.to_h["sample_data"]["breadcrumbs"]).to be_empty
442
+ expect(log_contents(log)).to contains_log(
443
+ :error,
444
+ "add_breadcrumb: Cannot add breadcrumb. The given metadata argument is not a Hash."
445
+ )
446
+ end
447
+ end
435
448
  end
436
449
 
437
450
  describe "#set_action" do
@@ -623,6 +636,18 @@ describe Appsignal::Transaction do
623
636
  expect(transaction.to_h["metadata"]).to eq("request_method" => "GET")
624
637
  end
625
638
 
639
+ context "when filter_metadata includes metadata key" do
640
+ before { Appsignal.config[:filter_metadata] = ["filter_key"] }
641
+ after { Appsignal.config[:filter_metadata] = [] }
642
+
643
+ it "does not set the metadata on the transaction" do
644
+ transaction.set_metadata(:filter_key, "filtered value")
645
+ transaction.set_metadata("filter_key", "filtered value")
646
+
647
+ expect(transaction.to_h["metadata"].keys).to_not include("filter_key")
648
+ end
649
+ end
650
+
626
651
  context "when the key is nil" do
627
652
  it "does not update the metadata on the transaction" do
628
653
  transaction.set_metadata(nil, "GET")
@@ -1275,8 +1300,8 @@ describe Appsignal::Transaction do
1275
1300
  end
1276
1301
  end
1277
1302
 
1278
- describe "#metadata" do
1279
- subject { transaction.send(:metadata) }
1303
+ describe "#sanitized_metadata" do
1304
+ subject { transaction.send(:sanitized_metadata) }
1280
1305
 
1281
1306
  context "when request is nil" do
1282
1307
  let(:request) { nil }
@@ -1291,9 +1316,18 @@ describe Appsignal::Transaction do
1291
1316
  end
1292
1317
 
1293
1318
  context "when env is present" do
1294
- let(:env) { { :metadata => { :key => "value" } } }
1319
+ let(:env) { { "key" => "value" } }
1320
+
1321
+ it { is_expected.to eq("key" => "value") }
1295
1322
 
1296
- it { is_expected.to eq env[:metadata] }
1323
+ context "with filter_metadata option set" do
1324
+ before { Appsignal.config[:filter_metadata] = ["key"] }
1325
+ after { Appsignal.config[:filter_metadata] = [] }
1326
+
1327
+ it "filters out keys listed in the filter_metadata option" do
1328
+ expect(subject.keys).to_not include("key")
1329
+ end
1330
+ end
1297
1331
  end
1298
1332
  end
1299
1333
 
@@ -1,5 +1,7 @@
1
1
  describe Appsignal::Utils::HashSanitizer do
2
2
  let(:file) { uploaded_file }
3
+ let(:some_array) { [1, 2, 3] }
4
+ let(:some_hash) { { :a => 1, :b => 2 } }
3
5
  let(:params) do
4
6
  {
5
7
  :text => "string",
@@ -8,6 +10,10 @@ describe Appsignal::Utils::HashSanitizer do
8
10
  :float => 0.0,
9
11
  :bool_true => true,
10
12
  :bool_false => false,
13
+ # Non-recursive appearances of the same array instance
14
+ :some_arrays => [some_array, some_array],
15
+ # Non-recursive appearances of the same hash instance
16
+ :some_hashes => { :a => some_hash, :b => some_hash },
11
17
  :nil => nil,
12
18
  :int => 1, # Fixnum
13
19
  :int64 => 1 << 64, # Bignum
@@ -20,9 +26,20 @@ describe Appsignal::Utils::HashSanitizer do
20
26
  {
21
27
  :key => "value",
22
28
  :file => file
23
- }
24
- ]
25
- }
29
+ }.tap do |hsh|
30
+ # Recursive hash-in-hash (should be [:nested_array][3][:recursive_hash])
31
+ hsh[:recursive_hash] = hsh
32
+ end
33
+ ].tap do |ary|
34
+ # Recursive array-in-array (should be [:nested_array][4])
35
+ ary << ary
36
+ # Recursive array-in-hash (should be [:nested_array][3][:recursive_array])
37
+ ary[3][:recursive_array] = ary
38
+ end
39
+ }.tap do |hsh|
40
+ # Recursive hash-in-array (should be [:nested_array][5])
41
+ hsh[:nested_array] << hsh
42
+ end
26
43
  }
27
44
  end
28
45
 
@@ -43,6 +60,9 @@ describe Appsignal::Utils::HashSanitizer do
43
60
  expect(subject[:nil]).to be_nil
44
61
  expect(subject[:int]).to eq(1)
45
62
  expect(subject[:int64]).to eq(1 << 64)
63
+ expect(subject[:some_arrays]).to eq([[1, 2, 3], [1, 2, 3]])
64
+ expect(subject[:some_hashes]).to eq({ :a => { :a => 1, :b => 2 },
65
+ :b => { :a => 1, :b => 2 } })
46
66
  end
47
67
 
48
68
  it "does not change the original params" do
@@ -72,7 +92,7 @@ describe Appsignal::Utils::HashSanitizer do
72
92
  expect(subject[2]).to include "::UploadedFile"
73
93
  end
74
94
 
75
- describe ":nested_hash key" do
95
+ describe "nested hash" do
76
96
  subject { sanitized_params[:hash][:nested_array][3] }
77
97
 
78
98
  it "returns a sanitized Hash" do
@@ -82,6 +102,24 @@ describe Appsignal::Utils::HashSanitizer do
82
102
  expect(subject[:file]).to be_instance_of String
83
103
  expect(subject[:file]).to include "::UploadedFile"
84
104
  end
105
+
106
+ it "replaces a recursive array" do
107
+ expect(subject[:recursive_array]).to eq("[RECURSIVE VALUE]")
108
+ end
109
+
110
+ it "replaces a recursive hash" do
111
+ expect(subject[:recursive_hash]).to eq("[RECURSIVE VALUE]")
112
+ end
113
+ end
114
+
115
+ describe "nested array" do
116
+ it "replaces a recursive array" do
117
+ expect(sanitized_params[:hash][:nested_array][4]).to eq("[RECURSIVE VALUE]")
118
+ end
119
+
120
+ it "replaces a recursive hash" do
121
+ expect(sanitized_params[:hash][:nested_array][5]).to eq("[RECURSIVE VALUE]")
122
+ end
85
123
  end
86
124
  end
87
125
  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.4.4
4
+ version: 3.4.6
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: 2023-06-02 00:00:00.000000000 Z
13
+ date: 2023-07-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -453,7 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
453
453
  - !ruby/object:Gem::Version
454
454
  version: '0'
455
455
  requirements: []
456
- rubygems_version: 3.4.11
456
+ rubygems_version: 3.3.7
457
457
  signing_key:
458
458
  specification_version: 4
459
459
  summary: Logs performance and exception data from your app to appsignal.com