appsignal 3.13.0-java → 4.0.0.beta.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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +499 -487
  3. data/CHANGELOG.md +121 -0
  4. data/Rakefile +31 -7
  5. data/benchmark.rake +4 -6
  6. data/build_matrix.yml +45 -39
  7. data/ext/agent.rb +27 -27
  8. data/ext/appsignal_extension.c +25 -0
  9. data/gemfiles/rails-7.2.gemfile +11 -0
  10. data/lib/appsignal/check_in/cron.rb +2 -15
  11. data/lib/appsignal/cli/diagnose.rb +37 -28
  12. data/lib/appsignal/cli/install.rb +5 -1
  13. data/lib/appsignal/config.rb +57 -119
  14. data/lib/appsignal/demo.rb +2 -2
  15. data/lib/appsignal/extension/jruby.rb +14 -0
  16. data/lib/appsignal/helpers/instrumentation.rb +152 -416
  17. data/lib/appsignal/helpers/metrics.rb +0 -16
  18. data/lib/appsignal/hooks/action_cable.rb +8 -8
  19. data/lib/appsignal/hooks/active_job.rb +2 -2
  20. data/lib/appsignal/hooks/at_exit.rb +37 -0
  21. data/lib/appsignal/hooks.rb +1 -16
  22. data/lib/appsignal/integrations/action_cable.rb +2 -2
  23. data/lib/appsignal/integrations/capistrano/appsignal.cap +2 -4
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -4
  25. data/lib/appsignal/integrations/delayed_job_plugin.rb +3 -3
  26. data/lib/appsignal/integrations/que.rb +2 -2
  27. data/lib/appsignal/integrations/railtie.rb +26 -59
  28. data/lib/appsignal/integrations/rake.rb +2 -2
  29. data/lib/appsignal/integrations/resque.rb +2 -2
  30. data/lib/appsignal/integrations/shoryuken.rb +4 -4
  31. data/lib/appsignal/integrations/sidekiq.rb +3 -3
  32. data/lib/appsignal/integrations/webmachine.rb +2 -2
  33. data/lib/appsignal/loaders.rb +1 -1
  34. data/lib/appsignal/probes.rb +0 -9
  35. data/lib/appsignal/rack/abstract_middleware.rb +4 -26
  36. data/lib/appsignal/rack/event_handler.rb +4 -4
  37. data/lib/appsignal/rack/rails_instrumentation.rb +1 -1
  38. data/lib/appsignal/rack.rb +0 -25
  39. data/lib/appsignal/sample_data.rb +108 -0
  40. data/lib/appsignal/transaction.rb +241 -359
  41. data/lib/appsignal/utils/rails_helper.rb +4 -0
  42. data/lib/appsignal/version.rb +1 -1
  43. data/lib/appsignal.rb +19 -71
  44. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  45. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  46. data/spec/lib/appsignal/capistrano3_spec.rb +53 -13
  47. data/spec/lib/appsignal/check_in_spec.rb +1 -207
  48. data/spec/lib/appsignal/cli/demo_spec.rb +7 -27
  49. data/spec/lib/appsignal/cli/diagnose_spec.rb +145 -110
  50. data/spec/lib/appsignal/config_spec.rb +304 -379
  51. data/spec/lib/appsignal/extension_install_failure_spec.rb +5 -1
  52. data/spec/lib/appsignal/extension_spec.rb +5 -1
  53. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +1 -1
  54. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +1 -2
  55. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +1 -0
  56. data/spec/lib/appsignal/hooks/activejob_spec.rb +7 -12
  57. data/spec/lib/appsignal/hooks/at_exit_spec.rb +72 -0
  58. data/spec/lib/appsignal/hooks/gvl_spec.rb +10 -5
  59. data/spec/lib/appsignal/hooks/http_spec.rb +3 -3
  60. data/spec/lib/appsignal/hooks/net_http_spec.rb +3 -3
  61. data/spec/lib/appsignal/hooks/rake_spec.rb +6 -9
  62. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -10
  63. data/spec/lib/appsignal/hooks/redis_spec.rb +4 -7
  64. data/spec/lib/appsignal/hooks/resque_spec.rb +3 -5
  65. data/spec/lib/appsignal/hooks_spec.rb +0 -41
  66. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +29 -20
  67. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +4 -9
  68. data/spec/lib/appsignal/integrations/railtie_spec.rb +179 -157
  69. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +3 -5
  70. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +48 -62
  71. data/spec/lib/appsignal/loaders/hanami_spec.rb +6 -9
  72. data/spec/lib/appsignal/loaders/padrino_spec.rb +6 -10
  73. data/spec/lib/appsignal/loaders/sinatra_spec.rb +6 -9
  74. data/spec/lib/appsignal/loaders_spec.rb +8 -1
  75. data/spec/lib/appsignal/marker_spec.rb +1 -1
  76. data/spec/lib/appsignal/probes_spec.rb +4 -83
  77. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +4 -63
  78. data/spec/lib/appsignal/rack/event_handler_spec.rb +18 -15
  79. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +3 -11
  80. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +4 -5
  81. data/spec/lib/appsignal/sample_data_spec.rb +213 -0
  82. data/spec/lib/appsignal/transaction_spec.rb +817 -1046
  83. data/spec/lib/appsignal/transmitter_spec.rb +6 -8
  84. data/spec/lib/appsignal_spec.rb +311 -643
  85. data/spec/spec_helper.rb +1 -3
  86. data/spec/support/fixtures/projects/valid/config/appsignal.yml +4 -7
  87. data/spec/support/fixtures/projects/valid_with_rails_app/config/application.rb +16 -0
  88. data/spec/support/fixtures/projects/valid_with_rails_app/config/appsignal.yml +56 -0
  89. data/spec/support/fixtures/projects/valid_with_rails_app/config/environment.rb +5 -0
  90. data/spec/support/helpers/api_request_helper.rb +3 -2
  91. data/spec/support/helpers/config_helpers.rb +41 -11
  92. data/spec/support/helpers/dependency_helper.rb +8 -0
  93. data/spec/support/helpers/log_helpers.rb +1 -0
  94. data/spec/support/helpers/rails_helper.rb +6 -6
  95. data/spec/support/helpers/transaction_helpers.rb +2 -24
  96. data/spec/support/matchers/transaction.rb +3 -3
  97. data/spec/support/mocks/appsignal_mock.rb +3 -3
  98. data/spec/support/mocks/mock_probe.rb +2 -0
  99. data/spec/support/testing.rb +2 -2
  100. metadata +12 -22
  101. data/gemfiles/que_beta.gemfile +0 -5
  102. data/lib/appsignal/helpers/heartbeat.rb +0 -20
  103. data/lib/appsignal/integrations/grape.rb +0 -35
  104. data/lib/appsignal/integrations/hanami.rb +0 -13
  105. data/lib/appsignal/integrations/padrino.rb +0 -13
  106. data/lib/appsignal/integrations/sinatra.rb +0 -13
  107. data/lib/appsignal/rack/generic_instrumentation.rb +0 -22
  108. data/lib/appsignal/rack/streaming_listener.rb +0 -28
  109. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -36
  110. data/spec/lib/appsignal/integrations/hanami_spec.rb +0 -17
  111. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -15
  112. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -15
  113. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +0 -81
  114. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +0 -69
  115. data/spec/support/fixtures/projects/valid/config/environments/development.rb +0 -0
  116. data/spec/support/fixtures/projects/valid/config/environments/production.rb +0 -0
  117. data/spec/support/fixtures/projects/valid/config/environments/test.rb +0 -0
  118. data/spec/support/rails/my_app.rb +0 -6
  119. /data/spec/support/fixtures/projects/{valid/config/application.rb → valid_with_rails_app/log/.gitkeep} +0 -0
@@ -1,8 +1,9 @@
1
1
  describe Appsignal::Transaction do
2
+ let(:options) { {} }
2
3
  let(:time) { Time.at(fixed_time) }
3
4
 
4
5
  before do
5
- start_agent
6
+ start_agent(:options => options)
6
7
  Timecop.freeze(time)
7
8
  end
8
9
  after { Timecop.return }
@@ -32,86 +33,13 @@ describe Appsignal::Transaction do
32
33
  transaction = create_transaction
33
34
  expect(transaction).to eq current_transaction
34
35
  end
36
+ end
35
37
 
36
- context "with legacy arguments" do
37
- it "returns the created transaction" do
38
- transaction_id = "mock-id"
39
- namespace = "my_namespace"
40
- transaction = legacy_create_transaction(
41
- :id => transaction_id,
42
- :namespace => namespace
43
- )
44
- expect(transaction).to be_a(Appsignal::Transaction)
45
-
46
- expect(transaction).to have_id(transaction_id)
47
- expect(transaction.transaction_id).to eq(transaction_id)
48
-
49
- expect(transaction).to have_namespace(namespace)
50
- expect(transaction.namespace).to eq(namespace)
51
- end
52
-
53
- it "logs deprecation warnings" do
54
- logs =
55
- capture_logs do
56
- legacy_create_transaction(
57
- :id => "mock-id",
58
- :namespace => "my_namespace",
59
- :request => Appsignal::Transaction::InternalGenericRequest.new({}),
60
- :options => { :force => true }
61
- )
62
- end
63
-
64
- expect(logs).to contains_log(
65
- :warn,
66
- "Appsignal::Transaction.create: " \
67
- "A new Transaction is created using the transaction ID argument."
68
- )
69
- expect(logs).to contains_log(
70
- :warn,
71
- "Appsignal::Transaction.create: " \
72
- "A Transaction is created using the namespace argument."
73
- )
74
- expect(logs).to contains_log(
75
- :warn,
76
- "Appsignal::Transaction.create: " \
77
- "A Transaction is created using the request argument."
78
- )
79
- expect(logs).to contains_log(
80
- :warn,
81
- "Appsignal::Transaction.create: " \
82
- "A Transaction is created using the `:force => true` option argument. "
83
- )
84
- end
85
-
86
- it "prints deprecation warnings" do
87
- err_stream = std_stream
88
- capture_std_streams(std_stream, err_stream) do
89
- legacy_create_transaction(
90
- :id => "mock-id",
91
- :namespace => "my_namespace",
92
- :request => Appsignal::Transaction::InternalGenericRequest.new({}),
93
- :options => { :force => true }
94
- )
95
- end
38
+ context "when an explicit extension transaction is passed in the initialiser" do
39
+ let(:ext) { "some_ext" }
96
40
 
97
- stderr = err_stream.read
98
- expect(stderr).to include(
99
- "appsignal WARNING: Appsignal::Transaction.create: " \
100
- "A new Transaction is created using the transaction ID argument."
101
- )
102
- expect(stderr).to include(
103
- "appsignal WARNING: Appsignal::Transaction.create: " \
104
- "A Transaction is created using the namespace argument."
105
- )
106
- expect(stderr).to include(
107
- "appsignal WARNING: Appsignal::Transaction.create: " \
108
- "A Transaction is created using the request argument."
109
- )
110
- expect(stderr).to include(
111
- "appsignal WARNING: Appsignal::Transaction.create: " \
112
- "A Transaction is created using the `:force => true` option argument. "
113
- )
114
- end
41
+ it "assigns the extension transaction to the transaction" do
42
+ expect(new_transaction(:ext => ext).ext).to be(ext)
115
43
  end
116
44
  end
117
45
 
@@ -137,28 +65,9 @@ describe Appsignal::Transaction do
137
65
  logs = capture_logs { create_transaction }
138
66
 
139
67
  expect(logs).to contains_log :warn,
140
- "Trying to start new transaction with id 'transaction_id_2', but a " \
141
- "transaction with id 'transaction_id_1' is already " \
142
- "running. Using transaction 'transaction_id_1'."
143
- end
144
-
145
- context "with option :force => true" do
146
- it "returns the newly created (and current) transaction" do
147
- original_transaction = create_transaction
148
-
149
- expect(original_transaction).to be_a(Appsignal::Transaction)
150
- expect(current_transaction).to have_id("transaction_id_1")
151
-
152
- new_transaction = legacy_create_transaction(
153
- :id => "transaction_id_2",
154
- :options => { :force => true }
155
- )
156
-
157
- expect(new_transaction).to be_a(Appsignal::Transaction)
158
- expect(new_transaction).to_not eq(original_transaction)
159
- expect(new_transaction).to have_id("transaction_id_2")
160
- expect(current_transaction).to eq(new_transaction)
161
- end
68
+ "Trying to start new transaction, but a transaction with id " \
69
+ "'transaction_id_1' is already running. " \
70
+ "Using transaction 'transaction_id_1'."
162
71
  end
163
72
  end
164
73
  end
@@ -252,7 +161,7 @@ describe Appsignal::Transaction do
252
161
 
253
162
  context "when transaction is being sampled" do
254
163
  it "samples data" do
255
- transaction.set_tags(:foo => "bar")
164
+ transaction.add_tags(:foo => "bar")
256
165
  keep_transactions { transaction.complete }
257
166
  expect(transaction).to include_tags("foo" => "bar")
258
167
  end
@@ -299,6 +208,306 @@ describe Appsignal::Transaction do
299
208
  end
300
209
  end
301
210
  end
211
+
212
+ context "when a transaction has errors" do
213
+ let(:error) do
214
+ e = ExampleStandardError.new("test message")
215
+ allow(e).to receive(:backtrace).and_return(["line 1"])
216
+ e
217
+ end
218
+
219
+ let(:other_error) do
220
+ e = ExampleStandardError.new("other test message")
221
+ allow(e).to receive(:backtrace).and_return(["line 2"])
222
+ e
223
+ end
224
+
225
+ context "when an error is already set on the transaction" do
226
+ it "reports errors as duplicate transactions" do
227
+ transaction.set_error(error)
228
+ transaction.add_error(other_error)
229
+
230
+ expect do
231
+ transaction.complete
232
+ end.to change { created_transactions.count }.from(1).to(2)
233
+
234
+ original_transaction, duplicate_transaction = created_transactions
235
+
236
+ expect(original_transaction).to have_error(
237
+ "ExampleStandardError",
238
+ "test message",
239
+ ["line 1"]
240
+ )
241
+ expect(original_transaction).to be_completed
242
+
243
+ expect(duplicate_transaction).to have_error(
244
+ "ExampleStandardError",
245
+ "other test message",
246
+ ["line 2"]
247
+ )
248
+ expect(duplicate_transaction).to be_completed
249
+ end
250
+ end
251
+
252
+ context "when no error is set on the transaction" do
253
+ it "reports the first error in the original transaction" do
254
+ transaction.add_error(error)
255
+ transaction.add_error(other_error)
256
+
257
+ expect do
258
+ transaction.complete
259
+ end.to change { created_transactions.count }.from(1).to(2)
260
+
261
+ original_transaction, duplicate_transaction = created_transactions
262
+
263
+ expect(original_transaction).to have_error(
264
+ "ExampleStandardError",
265
+ "test message",
266
+ ["line 1"]
267
+ )
268
+ expect(original_transaction).to be_completed
269
+
270
+ expect(duplicate_transaction).to have_error(
271
+ "ExampleStandardError",
272
+ "other test message",
273
+ ["line 2"]
274
+ )
275
+ expect(duplicate_transaction).to be_completed
276
+ end
277
+ end
278
+
279
+ it "stores the last reported errors" do
280
+ transaction.add_error(error)
281
+ transaction.add_error(other_error)
282
+ transaction.complete
283
+
284
+ expect(Appsignal::Transaction.last_errors).to contain_exactly(error, other_error)
285
+ end
286
+
287
+ describe "metadata" do
288
+ let(:tags) { { "tag" => "value" } }
289
+ let(:params) { { "param" => "value" } }
290
+ let(:headers) { { "REQUEST_METHOD" => "value" } }
291
+ let(:session_data) { { "session_data" => "value" } }
292
+ let(:custom_data) { { "custom_data" => "value" } }
293
+ before do
294
+ transaction.set_namespace("My namespace")
295
+ transaction.set_action("My action")
296
+ transaction.set_metadata("path", "/some/path")
297
+ transaction.set_metadata("method", "GET")
298
+ transaction.add_tags(tags)
299
+ transaction.add_params(params)
300
+ transaction.add_headers(headers)
301
+ transaction.add_session_data(session_data)
302
+ transaction.add_custom_data(custom_data)
303
+ transaction.add_breadcrumb("category", "action", "message", { "meta" => "data" })
304
+
305
+ transaction.start_event
306
+ transaction.finish_event("name", "title", "body", 1)
307
+
308
+ transaction.add_error(error)
309
+ transaction.add_error(other_error)
310
+
311
+ transaction.complete
312
+ end
313
+
314
+ it "copies the transaction metadata and sample data on the duplicate transaction" do
315
+ original_transaction, duplicate_transaction = created_transactions
316
+
317
+ duplicate_hash = duplicate_transaction.to_h.tap do |h|
318
+ h.delete("id")
319
+ h.delete("error")
320
+ end
321
+ original_hash = original_transaction.to_h.tap do |h|
322
+ h.delete("id")
323
+ h.delete("error")
324
+ end
325
+ expect(duplicate_hash).to eq(original_hash)
326
+ end
327
+
328
+ it "the duplicate transaction has a different transaction id" do
329
+ original_transaction, duplicate_transaction = created_transactions
330
+
331
+ expect(original_transaction.transaction_id)
332
+ .to_not eq(duplicate_transaction.transaction_id)
333
+ end
334
+
335
+ it "the duplicate transaction has a different extension transaction than the original" do
336
+ original_transaction, duplicate_transaction = created_transactions
337
+
338
+ expect(original_transaction.ext).to_not eq(duplicate_transaction.ext)
339
+ end
340
+
341
+ it "sets is_duplicate set to true on the duplicate transaction" do
342
+ original_transaction, duplicate_transaction = created_transactions
343
+
344
+ expect(original_transaction.is_duplicate).to be(false)
345
+ expect(duplicate_transaction.is_duplicate).to be(true)
346
+ end
347
+ end
348
+
349
+ it "merges sample data from the original transaction in the duplicate transaction" do
350
+ transaction.add_tags("root" => "tag")
351
+ transaction.add_params("root" => "param")
352
+ transaction.add_session_data("root" => "session")
353
+ transaction.add_headers("REQUEST_METHOD" => "root")
354
+ transaction.add_custom_data("root" => "custom")
355
+ transaction.add_breadcrumb("root", "breadcrumb")
356
+ Appsignal.report_error(error) do |t|
357
+ t.add_tags("original" => "tag")
358
+ t.add_params("original" => "param")
359
+ t.add_session_data("original" => "session")
360
+ t.add_headers("REQUEST_PATH" => "/original")
361
+ t.add_custom_data("original" => "custom")
362
+ t.add_breadcrumb("original", "breadcrumb")
363
+ end
364
+ Appsignal.report_error(other_error) do |t|
365
+ t.add_tags("duplicate" => "tag")
366
+ t.add_params("duplicate" => "param")
367
+ t.add_session_data("duplicate" => "session")
368
+ t.add_headers("HTTP_ACCEPT" => "duplicate")
369
+ t.add_custom_data("duplicate" => "custom")
370
+ t.add_breadcrumb("duplicate", "breadcrumb")
371
+ end
372
+ transaction.add_tags("root2" => "tag")
373
+ transaction.add_params("root2" => "param")
374
+ transaction.add_session_data("root2" => "session")
375
+ transaction.add_headers("PATH_INFO" => "/root2")
376
+ transaction.add_custom_data("root2" => "custom")
377
+ transaction.add_breadcrumb("root2", "breadcrumb")
378
+ transaction.complete
379
+
380
+ original_transaction, duplicate_transaction = created_transactions
381
+ # Original
382
+ expect(original_transaction).to include_tags(
383
+ "root" => "tag",
384
+ "original" => "tag",
385
+ "root2" => "tag"
386
+ )
387
+ expect(original_transaction).to_not include_tags("duplicate" => anything)
388
+ expect(original_transaction).to include_params(
389
+ "root" => "param",
390
+ "original" => "param",
391
+ "root2" => "param"
392
+ )
393
+ expect(original_transaction).to_not include_params("duplicate" => anything)
394
+ expect(original_transaction).to include_session_data(
395
+ "root" => "session",
396
+ "original" => "session",
397
+ "root2" => "session"
398
+ )
399
+ expect(original_transaction).to_not include_session_data("duplicate" => anything)
400
+ expect(original_transaction).to include_environment(
401
+ "REQUEST_METHOD" => "root",
402
+ "REQUEST_PATH" => "/original",
403
+ "PATH_INFO" => "/root2"
404
+ )
405
+ expect(original_transaction).to_not include_environment("HTTP_ACCEPT" => anything)
406
+ expect(original_transaction).to include_custom_data(
407
+ "root" => "custom",
408
+ "original" => "custom",
409
+ "root2" => "custom"
410
+ )
411
+ expect(original_transaction).to_not include_custom_data("duplicate" => anything)
412
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "root")
413
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "original")
414
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "root2")
415
+ expect(original_transaction).to_not include_breadcrumb("breadcrumb", "duplicate")
416
+
417
+ # Duplicate
418
+ expect(duplicate_transaction).to include_tags(
419
+ "root" => "tag",
420
+ "duplicate" => "tag",
421
+ "root2" => "tag"
422
+ )
423
+ expect(duplicate_transaction).to_not include_tags("original" => anything)
424
+ expect(duplicate_transaction).to include_params(
425
+ "root" => "param",
426
+ "duplicate" => "param",
427
+ "root2" => "param"
428
+ )
429
+ expect(duplicate_transaction).to_not include_params("original" => anything)
430
+ expect(duplicate_transaction).to include_session_data(
431
+ "root" => "session",
432
+ "duplicate" => "session",
433
+ "root2" => "session"
434
+ )
435
+ expect(duplicate_transaction).to_not include_session_data("original" => anything)
436
+ expect(duplicate_transaction).to include_environment(
437
+ "PATH_INFO" => "/root2",
438
+ "HTTP_ACCEPT" => "duplicate",
439
+ "REQUEST_METHOD" => "root"
440
+ )
441
+ expect(duplicate_transaction).to_not include_environment("REQUEST_PATH" => anything)
442
+ expect(duplicate_transaction).to include_custom_data(
443
+ "root" => "custom",
444
+ "duplicate" => "custom",
445
+ "root2" => "custom"
446
+ )
447
+ expect(duplicate_transaction).to_not include_custom_data("original" => anything)
448
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "root")
449
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "duplicate")
450
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "root2")
451
+ expect(duplicate_transaction).to_not include_breadcrumb("breadcrumb", "original")
452
+ end
453
+
454
+ it "overrides sample data from the original transaction in the duplicate transaction" do
455
+ transaction.add_tags("changeme" => "tag")
456
+ transaction.add_params("changeme" => "param")
457
+ transaction.add_session_data("changeme" => "session")
458
+ transaction.add_headers("REQUEST_METHOD" => "root")
459
+ transaction.add_custom_data("changeme" => "custom")
460
+ Appsignal.report_error(error)
461
+ Appsignal.report_error(other_error) do |t|
462
+ t.add_tags("changeme" => "duplicate_tag")
463
+ t.add_params("changeme" => "duplicate_param")
464
+ t.add_session_data("changeme" => "duplicate_session")
465
+ t.add_headers("REQUEST_METHOD" => "duplicate")
466
+ t.add_custom_data("changeme" => "duplicate_custom")
467
+ end
468
+ transaction.add_tags("changeme" => "changed_tag")
469
+ transaction.add_params("changeme" => "changed_param")
470
+ transaction.add_session_data("changeme" => "changed_session")
471
+ transaction.add_headers("REQUEST_METHOD" => "changed")
472
+ transaction.add_custom_data("changeme" => "changed_custom")
473
+ transaction.complete
474
+
475
+ original_transaction, duplicate_transaction = created_transactions
476
+ # Original
477
+ expect(original_transaction).to include_tags(
478
+ "changeme" => "changed_tag"
479
+ )
480
+ expect(original_transaction).to include_params(
481
+ "changeme" => "changed_param"
482
+ )
483
+ expect(original_transaction).to include_session_data(
484
+ "changeme" => "changed_session"
485
+ )
486
+ expect(original_transaction).to include_environment(
487
+ "REQUEST_METHOD" => "changed"
488
+ )
489
+ expect(original_transaction).to include_custom_data(
490
+ "changeme" => "changed_custom"
491
+ )
492
+
493
+ # Duplicate
494
+ expect(duplicate_transaction).to include_tags(
495
+ "changeme" => "duplicate_tag"
496
+ )
497
+ expect(duplicate_transaction).to include_params(
498
+ "changeme" => "duplicate_param"
499
+ )
500
+ expect(duplicate_transaction).to include_session_data(
501
+ "changeme" => "duplicate_session"
502
+ )
503
+ expect(duplicate_transaction).to include_environment(
504
+ "REQUEST_METHOD" => "duplicate"
505
+ )
506
+ expect(duplicate_transaction).to include_custom_data(
507
+ "changeme" => "duplicate_custom"
508
+ )
509
+ end
510
+ end
302
511
  end
303
512
 
304
513
  context "pausing" do
@@ -363,49 +572,9 @@ describe Appsignal::Transaction do
363
572
  end
364
573
  end
365
574
 
366
- context "transaction id" do
367
- before do
368
- allow(SecureRandom).to receive(:uuid).and_return("mock_transaction_id")
369
- end
370
-
371
- it "sets the transaction id" do
372
- expect(transaction).to have_id("mock_transaction_id")
373
- end
374
- end
375
-
376
575
  it "sets the namespace to http_request" do
377
576
  expect(transaction.namespace).to eq "http_request"
378
577
  end
379
-
380
- it "sets the request" do
381
- expect(transaction.request).to be_a(Appsignal::Transaction::InternalGenericRequest)
382
- end
383
-
384
- it "sets the request not to paused" do
385
- expect(transaction.paused?).to be_falsy
386
- end
387
-
388
- it "sets no tags by default" do
389
- expect(transaction.tags).to be_empty
390
- end
391
-
392
- describe "#options" do
393
- let(:options) { transaction.options }
394
-
395
- it "sets the default :params_method" do
396
- expect(options[:params_method]).to eq :params
397
- end
398
-
399
- context "with overridden options" do
400
- let(:transaction) do
401
- legacy_new_transaction(:options => { :params_method => :filtered_params })
402
- end
403
-
404
- it "sets the overridden :params_method" do
405
- expect(options[:params_method]).to eq :filtered_params
406
- end
407
- end
408
- end
409
578
  end
410
579
 
411
580
  describe "#store" do
@@ -423,209 +592,242 @@ describe Appsignal::Transaction do
423
592
  end
424
593
  end
425
594
 
426
- describe "#params" do
595
+ describe "#add_params" do
427
596
  let(:transaction) { new_transaction }
428
- let(:params) { transaction.params }
429
597
 
430
- context "with custom params set on transaction" do
431
- before { transaction.set_params(:foo => "bar") }
598
+ it "has a #set_params alias" do
599
+ expect(transaction.method(:add_params)).to eq(transaction.method(:set_params))
600
+ end
601
+
602
+ it "adds the params to the transaction" do
603
+ params = { "key" => "value" }
604
+ transaction.add_params(params)
432
605
 
433
- it "returns custom parameters" do
434
- expect(params).to eq(:foo => "bar")
435
- end
606
+ transaction._sample
607
+ expect(transaction).to include_params(params)
608
+ end
436
609
 
437
- context "when params is a callable object" do
438
- it "calls the params object and sets the return value as parametesr" do
439
- transaction.set_params { { "param1" => "value1" } }
610
+ it "merges the params on the transaction" do
611
+ transaction.add_params("abc" => "value")
612
+ transaction.add_params("def" => "value")
613
+ transaction.add_params { { "xyz" => "value" } }
440
614
 
441
- expect(params).to eq("param1" => "value1")
442
- end
443
- end
615
+ transaction._sample
616
+ expect(transaction).to include_params(
617
+ "abc" => "value",
618
+ "def" => "value",
619
+ "xyz" => "value"
620
+ )
444
621
  end
445
622
 
446
- context "without custom params set on transaction" do
447
- let(:transaction) do
448
- legacy_new_transaction(
449
- :request => legacy_request(
450
- :params => {
451
- "action" => "show",
452
- "controller" => "blog_posts",
453
- "id" => "1"
623
+ it "adds the params to the transaction with a block" do
624
+ params = { "key" => "value" }
625
+ transaction.add_params { params }
454
626
 
455
- }
456
- )
457
- )
458
- end
627
+ transaction._sample
628
+ expect(transaction).to include_params(params)
629
+ end
459
630
 
460
- it "returns parameters from request" do
461
- expect(params).to eq(
462
- "action" => "show",
463
- "controller" => "blog_posts",
464
- "id" => "1"
465
- )
466
- end
631
+ it "adds the params block value when both an argument and block are given" do
632
+ arg_params = { "argument" => "value" }
633
+ block_params = { "block" => "value" }
634
+ transaction.add_params(arg_params) { block_params }
635
+
636
+ transaction._sample
637
+ expect(transaction).to include_params(block_params)
467
638
  end
468
- end
469
639
 
470
- describe "#params=" do
471
- let(:transaction) { new_transaction }
640
+ it "logs an error if an error occurred storing the params" do
641
+ transaction.add_params { raise "uh oh" }
642
+
643
+ logs = capture_logs { transaction._sample }
644
+ expect(logs).to contains_log(
645
+ :error,
646
+ "Exception while fetching params: RuntimeError: uh oh"
647
+ )
648
+ end
472
649
 
473
- it "sets params on the transaction" do
474
- params = { "foo" => "bar" }
475
- silence { transaction.params = params }
650
+ it "does not update the params on the transaction if the given value is nil" do
651
+ params = { "key" => "value" }
652
+ transaction.add_params(params)
653
+ transaction.add_params(nil)
476
654
 
477
655
  transaction._sample
478
- expect(transaction.params).to eq(params)
479
656
  expect(transaction).to include_params(params)
480
657
  end
481
658
 
482
- it "logs a deprecation warning" do
483
- logs =
484
- capture_logs do
485
- transaction.params = { "foo" => "bar" }
486
- end
659
+ context "with AppSignal filtering" do
660
+ let(:options) { { :filter_parameters => %w[foo] } }
487
661
 
488
- expect(logs).to contains_log(
489
- :warn,
490
- "Transaction#params= is deprecated." \
491
- "Use Transaction#set_params or #set_params_if_nil instead."
492
- )
662
+ it "returns sanitized custom params" do
663
+ transaction.add_params("foo" => "value", "baz" => "bat")
664
+
665
+ transaction._sample
666
+ expect(transaction).to include_params("foo" => "[FILTERED]", "baz" => "bat")
667
+ end
493
668
  end
494
669
  end
495
670
 
496
- describe "#set_params" do
671
+ describe "#add_params_if_nil" do
497
672
  let(:transaction) { new_transaction }
498
673
 
499
- context "when the params are set" do
500
- it "updates the params on the transaction" do
674
+ it "has a #set_params_if_nil alias" do
675
+ expect(transaction.method(:add_params_if_nil)).to eq(transaction.method(:set_params_if_nil))
676
+ end
677
+
678
+ context "when the params are not set" do
679
+ it "adds the params to the transaction" do
501
680
  params = { "key" => "value" }
502
- transaction.set_params(params)
681
+ transaction.add_params_if_nil(params)
503
682
 
504
683
  transaction._sample
505
- expect(transaction.params).to eq(params)
506
684
  expect(transaction).to include_params(params)
507
685
  end
508
686
 
509
- it "updates the params on the transaction with a block" do
687
+ it "adds the params to the transaction with a block" do
510
688
  params = { "key" => "value" }
511
- transaction.set_params { params }
689
+ transaction.add_params_if_nil { params }
512
690
 
513
691
  transaction._sample
514
- expect(transaction.params).to eq(params)
515
692
  expect(transaction).to include_params(params)
516
693
  end
517
694
 
518
- it "updates with the params argument when both an argument and block are given" do
695
+ it "adds the params block value when both an argument and block are given" do
519
696
  arg_params = { "argument" => "value" }
520
697
  block_params = { "block" => "value" }
521
- transaction.set_params(arg_params) { block_params }
698
+ transaction.add_params_if_nil(arg_params) { block_params }
522
699
 
523
700
  transaction._sample
524
- expect(transaction.params).to eq(arg_params)
525
- expect(transaction).to include_params(arg_params)
526
- end
527
-
528
- it "logs an error if an error occurred storing the params" do
529
- transaction.set_params { raise "uh oh" }
530
-
531
- logs = capture_logs { transaction._sample }
532
- expect(logs).to contains_log(
533
- :error,
534
- "Exception while fetching params: RuntimeError: uh oh"
535
- )
701
+ expect(transaction).to include_params(block_params)
536
702
  end
537
- end
538
703
 
539
- context "when the given params is nil" do
540
- it "does not update the params on the transaction" do
704
+ it "does not update the params on the transaction if the given value is nil" do
541
705
  params = { "key" => "value" }
542
- transaction.set_params(params)
543
- transaction.set_params(nil)
706
+ transaction.add_params(params)
707
+ transaction.add_params_if_nil(nil)
544
708
 
545
709
  transaction._sample
546
- expect(transaction.params).to eq(params)
547
710
  expect(transaction).to include_params(params)
548
711
  end
549
712
  end
550
- end
551
-
552
- describe "#set_params_if_nil" do
553
- let(:transaction) { new_transaction }
554
713
 
555
- context "when the params are not set" do
556
- it "sets the params on the transaction" do
714
+ context "when the params are set" do
715
+ it "does not update the params on the transaction" do
716
+ preset_params = { "other" => "params" }
557
717
  params = { "key" => "value" }
558
- transaction.set_params_if_nil(params)
718
+ transaction.add_params(preset_params)
719
+ transaction.add_params_if_nil(params)
559
720
 
560
721
  transaction._sample
561
- expect(transaction.params).to eq(params)
562
- expect(transaction).to include_params(params)
722
+ expect(transaction).to include_params(preset_params)
563
723
  end
564
724
 
565
- it "updates the params on the transaction with a block" do
725
+ it "does not update the params with a block on the transaction" do
726
+ preset_params = { "other" => "params" }
566
727
  params = { "key" => "value" }
567
- transaction.set_params_if_nil { params }
728
+ transaction.add_params(preset_params)
729
+ transaction.add_params_if_nil { params }
568
730
 
569
731
  transaction._sample
570
- expect(transaction.params).to eq(params)
571
- expect(transaction).to include_params(params)
732
+ expect(transaction).to include_params(preset_params)
572
733
  end
734
+ end
573
735
 
574
- it "updates with the params argument when both an argument and block are given" do
575
- arg_params = { "argument" => "value" }
576
- block_params = { "block" => "value" }
577
- transaction.set_params_if_nil(arg_params) { block_params }
736
+ context "when the params were set as an empty value" do
737
+ it "does not set params on the transaction" do
738
+ transaction.add_params("key1" => "value")
739
+ transaction.set_empty_params!
740
+ transaction.add_params_if_nil("key2" => "value")
578
741
 
579
742
  transaction._sample
580
- expect(transaction.params).to eq(arg_params)
581
- expect(transaction).to include_params(arg_params)
743
+ expect(transaction).to_not include_params
582
744
  end
745
+ end
746
+ end
583
747
 
584
- context "when the given params is nil" do
585
- it "does not update the params on the transaction" do
586
- params = { "key" => "value" }
587
- transaction.set_params(params)
588
- transaction.set_params_if_nil(nil)
748
+ describe "#add_session_data" do
749
+ let(:transaction) { new_transaction }
589
750
 
590
- transaction._sample
591
- expect(transaction.params).to eq(params)
592
- expect(transaction).to include_params(params)
593
- end
594
- end
751
+ it "has a #set_session_data alias" do
752
+ expect(transaction.method(:add_session_data)).to eq(transaction.method(:set_session_data))
595
753
  end
596
754
 
597
- context "when the params are set" do
598
- it "does not update the params on the transaction" do
599
- preset_params = { "other" => "params" }
600
- params = { "key" => "value" }
601
- transaction.set_params(preset_params)
602
- transaction.set_params_if_nil(params)
755
+ it "adds the session data to the transaction" do
756
+ data = { "key" => "value" }
757
+ transaction.add_session_data(data)
603
758
 
604
- transaction._sample
605
- expect(transaction.params).to eq(preset_params)
606
- expect(transaction).to include_params(preset_params)
607
- end
759
+ transaction._sample
760
+ expect(transaction).to include_session_data(data)
761
+ end
608
762
 
609
- it "does not update the params with a block on the transaction" do
610
- preset_params = { "other" => "params" }
611
- params = { "key" => "value" }
612
- transaction.set_params(preset_params)
613
- transaction.set_params_if_nil { params }
763
+ it "merges the session data on the transaction" do
764
+ transaction.add_session_data("abc" => "value")
765
+ transaction.add_session_data("def" => "value")
766
+ transaction.add_session_data { { "xyz" => "value" } }
767
+
768
+ transaction._sample
769
+ expect(transaction).to include_session_data(
770
+ "abc" => "value",
771
+ "def" => "value",
772
+ "xyz" => "value"
773
+ )
774
+ end
775
+
776
+ it "adds the session data to the transaction with a block" do
777
+ data = { "key" => "value" }
778
+ transaction.add_session_data { data }
779
+
780
+ transaction._sample
781
+ expect(transaction).to include_session_data(data)
782
+ end
783
+
784
+ it "adds the session data block value when both an argument and block are given" do
785
+ arg_data = { "argument" => "value" }
786
+ block_data = { "block" => "value" }
787
+ transaction.add_session_data(arg_data) { block_data }
788
+
789
+ transaction._sample
790
+ expect(transaction).to include_session_data(block_data)
791
+ end
792
+
793
+ it "logs an error if an error occurred storing the session data" do
794
+ transaction.add_session_data { raise "uh oh" }
795
+
796
+ logs = capture_logs { transaction._sample }
797
+ expect(logs).to contains_log(
798
+ :error,
799
+ "Exception while fetching session data: RuntimeError: uh oh"
800
+ )
801
+ end
802
+
803
+ it "does not update the session data on the transaction if the given value is nil" do
804
+ data = { "key" => "value" }
805
+ transaction.add_session_data(data)
806
+ transaction.add_session_data(nil)
807
+
808
+ transaction._sample
809
+ expect(transaction).to include_session_data(data)
810
+ end
811
+
812
+ context "with filter_session_data" do
813
+ let(:options) { { :filter_session_data => ["filtered_key"] } }
814
+
815
+ it "does not include filtered out session data" do
816
+ transaction.add_session_data("data" => "value1", "filtered_key" => "filtered_value")
614
817
 
615
818
  transaction._sample
616
- expect(transaction.params).to eq(preset_params)
617
- expect(transaction).to include_params(preset_params)
819
+ expect(transaction).to include_session_data("data" => "value1")
618
820
  end
619
821
  end
620
822
  end
621
823
 
622
- describe "#set_session_data" do
824
+ describe "#add_session_data_if_nil" do
623
825
  let(:transaction) { new_transaction }
624
826
 
625
- context "when the session data is set" do
626
- it "updates the session data on the transaction" do
827
+ context "when the session data is not set" do
828
+ it "sets the session data on the transaction" do
627
829
  data = { "key" => "value" }
628
- transaction.set_session_data(data)
830
+ transaction.add_session_data_if_nil(data)
629
831
 
630
832
  transaction._sample
631
833
  expect(transaction).to include_session_data(data)
@@ -633,109 +835,47 @@ describe Appsignal::Transaction do
633
835
 
634
836
  it "updates the session data on the transaction with a block" do
635
837
  data = { "key" => "value" }
636
- transaction.set_session_data { data }
838
+ transaction.add_session_data_if_nil { data }
637
839
 
638
840
  transaction._sample
639
841
  expect(transaction).to include_session_data(data)
640
842
  end
641
843
 
642
- it "updates with the session data argument when both an argument and block are given" do
844
+ it "updates with the session data block when both an argument and block are given" do
643
845
  arg_data = { "argument" => "value" }
644
846
  block_data = { "block" => "value" }
645
- transaction.set_session_data(arg_data) { block_data }
847
+ transaction.add_session_data_if_nil(arg_data) { block_data }
646
848
 
647
849
  transaction._sample
648
- expect(transaction).to include_session_data(arg_data)
850
+ expect(transaction).to include_session_data(block_data)
649
851
  end
650
852
 
651
- it "does not include filtered out session data" do
652
- Appsignal.config[:filter_session_data] = ["filtered_key"]
653
- transaction.set_session_data("data" => "value1", "filtered_key" => "filtered_value")
853
+ it "does not update the session data on the transaction if the given value is nil" do
854
+ data = { "key" => "value" }
855
+ transaction.add_session_data(data)
856
+ transaction.add_session_data_if_nil(nil)
654
857
 
655
858
  transaction._sample
656
- expect(transaction).to include_session_data("data" => "value1")
657
- end
658
-
659
- it "logs an error if an error occurred storing the session data" do
660
- transaction.set_session_data { raise "uh oh" }
661
-
662
- logs = capture_logs { transaction._sample }
663
- expect(logs).to contains_log(
664
- :error,
665
- "Exception while fetching session data: RuntimeError: uh oh"
666
- )
859
+ expect(transaction).to include_session_data(data)
667
860
  end
668
861
  end
669
862
 
670
- context "when the given session data is nil" do
863
+ context "when the session data are set" do
671
864
  it "does not update the session data on the transaction" do
672
- data = { "key" => "value" }
673
- transaction.set_session_data(data)
674
- transaction.set_session_data(nil)
675
-
676
- transaction._sample
677
- expect(transaction).to include_session_data(data)
678
- end
679
- end
680
- end
681
-
682
- describe "#set_session_data_if_nil" do
683
- let(:transaction) { new_transaction }
684
-
685
- context "when the params are not set" do
686
- it "sets the params on the transaction" do
687
- data = { "key" => "value" }
688
- transaction.set_session_data_if_nil(data)
689
-
690
- transaction._sample
691
- expect(transaction).to include_session_data(data)
692
- end
693
-
694
- it "updates the params on the transaction with a block" do
695
- data = { "key" => "value" }
696
- transaction.set_session_data_if_nil { data }
697
-
698
- transaction._sample
699
- expect(transaction).to include_session_data(data)
700
- end
701
-
702
- it "updates with the params argument when both an argument and block are given" do
703
- arg_data = { "argument" => "value" }
704
- block_data = { "block" => "value" }
705
- transaction.set_session_data_if_nil(arg_data) { block_data }
706
-
707
- transaction._sample
708
- expect(transaction).to include_session_data(arg_data)
709
- end
710
-
711
- context "when the given params is nil" do
712
- it "does not update the params on the transaction" do
713
- data = { "key" => "value" }
714
- transaction.set_session_data(data)
715
- transaction.set_session_data_if_nil(nil)
716
-
717
- transaction._sample
718
- expect(transaction).to include_session_data(data)
719
- end
720
- end
721
- end
722
-
723
- context "when the params are set" do
724
- it "does not update the params on the transaction" do
725
865
  preset_data = { "other" => "data" }
726
866
  data = { "key" => "value" }
727
- transaction.set_session_data(preset_data)
728
- transaction.set_session_data_if_nil(data)
867
+ transaction.add_session_data(preset_data)
868
+ transaction.add_session_data_if_nil(data)
729
869
 
730
870
  transaction._sample
731
871
  expect(transaction).to include_session_data(preset_data)
732
872
  end
733
873
 
734
- it "does not update the params with a block on the transaction" do
874
+ it "does not update the session data with a block on the transaction" do
735
875
  preset_data = { "other" => "data" }
736
876
  data = { "key" => "value" }
737
- transaction.set_session_data(preset_data)
738
- transaction.set_session_data_if_nil { data }
877
+ transaction.add_session_data(preset_data)
878
+ transaction.add_session_data_if_nil { data }
739
879
 
740
880
  transaction._sample
741
881
  expect(transaction).to include_session_data(preset_data)
@@ -743,123 +883,141 @@ describe Appsignal::Transaction do
743
883
  end
744
884
  end
745
885
 
746
- describe "#set_headers" do
886
+ describe "#add_headers" do
747
887
  let(:transaction) { new_transaction }
748
888
 
749
- context "when the headers are set" do
750
- it "updates the headers on the transaction" do
751
- headers = { "PATH_INFO" => "value" }
752
- transaction.set_headers(headers)
889
+ it "has a #set_headers alias" do
890
+ expect(transaction.method(:add_headers)).to eq(transaction.method(:set_headers))
891
+ end
753
892
 
754
- transaction._sample
755
- expect(transaction).to include_environment(headers)
756
- end
893
+ it "adds the headers to the transaction" do
894
+ headers = { "PATH_INFO" => "value" }
895
+ transaction.add_headers(headers)
757
896
 
758
- it "updates the headers on the transaction with a block" do
759
- headers = { "PATH_INFO" => "value" }
760
- transaction.set_headers { headers }
897
+ transaction._sample
898
+ expect(transaction).to include_environment(headers)
899
+ end
761
900
 
762
- transaction._sample
763
- expect(transaction).to include_environment(headers)
764
- end
901
+ it "merges the headers on the transaction" do
902
+ transaction.add_headers("PATH_INFO" => "value")
903
+ transaction.add_headers("REQUEST_METHOD" => "value")
904
+ transaction.add_headers { { "HTTP_ACCEPT" => "value" } }
765
905
 
766
- it "updates with the headers argument when both an argument and block are given" do
767
- arg_data = { "PATH_INFO" => "/arg-path" }
768
- block_data = { "PATH_INFO" => "/block-path" }
769
- transaction.set_headers(arg_data) { block_data }
906
+ transaction._sample
907
+ expect(transaction).to include_environment(
908
+ "PATH_INFO" => "value",
909
+ "REQUEST_METHOD" => "value",
910
+ "HTTP_ACCEPT" => "value"
911
+ )
912
+ end
770
913
 
771
- transaction._sample
772
- expect(transaction).to include_environment(arg_data)
773
- end
914
+ it "adds the headers to the transaction with a block" do
915
+ headers = { "PATH_INFO" => "value" }
916
+ transaction.add_headers { headers }
774
917
 
775
- it "does not include filtered out headers" do
776
- Appsignal.config[:request_headers] = ["MY_HEADER"]
777
- transaction.set_headers("MY_HEADER" => "value1", "filtered_key" => "filtered_value")
918
+ transaction._sample
919
+ expect(transaction).to include_environment(headers)
920
+ end
778
921
 
779
- transaction._sample
780
- expect(transaction).to include_environment("MY_HEADER" => "value1")
781
- end
922
+ it "adds the headers block value when both an argument and block are given" do
923
+ arg_data = { "PATH_INFO" => "/arg-path" }
924
+ block_data = { "PATH_INFO" => "/block-path" }
925
+ transaction.add_headers(arg_data) { block_data }
782
926
 
783
- it "logs an error if an error occurred storing the headers" do
784
- transaction.set_headers { raise "uh oh" }
927
+ transaction._sample
928
+ expect(transaction).to include_environment(block_data)
929
+ end
785
930
 
786
- logs = capture_logs { transaction._sample }
787
- expect(logs).to contains_log(
788
- :error,
789
- "Exception while fetching headers: RuntimeError: uh oh"
790
- )
791
- end
931
+ it "logs an error if an error occurred storing the headers" do
932
+ transaction.add_headers { raise "uh oh" }
933
+
934
+ logs = capture_logs { transaction._sample }
935
+ expect(logs).to contains_log(
936
+ :error,
937
+ "Exception while fetching headers: RuntimeError: uh oh"
938
+ )
792
939
  end
793
940
 
794
- context "when the given headers is nil" do
795
- it "does not update the headers on the transaction" do
796
- headers = { "PATH_INFO" => "value" }
797
- transaction.set_headers(headers)
798
- transaction.set_headers(nil)
941
+ it "does not update the headers on the transaction if the given value is nil" do
942
+ headers = { "PATH_INFO" => "value" }
943
+ transaction.add_headers(headers)
944
+ transaction.add_headers(nil)
945
+
946
+ transaction._sample
947
+ expect(transaction).to include_environment(headers)
948
+ end
949
+
950
+ context "with request_headers options" do
951
+ let(:options) { { :request_headers => ["MY_HEADER"] } }
952
+
953
+ it "does not include filtered out headers" do
954
+ transaction.add_headers("MY_HEADER" => "value1", "filtered_key" => "filtered_value")
799
955
 
800
956
  transaction._sample
801
- expect(transaction).to include_environment(headers)
957
+ expect(transaction).to include_environment("MY_HEADER" => "value1")
802
958
  end
803
959
  end
804
960
  end
805
961
 
806
- describe "#set_headers_if_nil" do
962
+ describe "#add_headers_if_nil" do
807
963
  let(:transaction) { new_transaction }
808
964
 
809
- context "when the params are not set" do
810
- it "sets the params on the transaction" do
965
+ it "has a #set_headers_if_nil alias" do
966
+ expect(transaction.method(:add_headers_if_nil)).to eq(transaction.method(:set_headers_if_nil))
967
+ end
968
+
969
+ context "when the headers are not set" do
970
+ it "adds the headers to the transaction" do
811
971
  headers = { "PATH_INFO" => "value" }
812
- transaction.set_headers_if_nil(headers)
972
+ transaction.add_headers_if_nil(headers)
813
973
 
814
974
  transaction._sample
815
975
  expect(transaction).to include_environment(headers)
816
976
  end
817
977
 
818
- it "updates the params on the transaction with a block" do
978
+ it "adds the headers to the transaction with a block" do
819
979
  headers = { "PATH_INFO" => "value" }
820
- transaction.set_headers_if_nil { headers }
980
+ transaction.add_headers_if_nil { headers }
821
981
 
822
982
  transaction._sample
823
983
  expect(transaction).to include_environment(headers)
824
984
  end
825
985
 
826
- it "updates with the params argument when both an argument and block are given" do
986
+ it "adds the headers block value when both an argument and block are given" do
827
987
  arg_data = { "PATH_INFO" => "/arg-path" }
828
988
  block_data = { "PATH_INFO" => "/block-path" }
829
- transaction.set_headers_if_nil(arg_data) { block_data }
989
+ transaction.add_headers_if_nil(arg_data) { block_data }
830
990
 
831
991
  transaction._sample
832
- expect(transaction).to include_environment(arg_data)
992
+ expect(transaction).to include_environment(block_data)
833
993
  end
834
994
 
835
- context "when the given params is nil" do
836
- it "does not update the params on the transaction" do
837
- headers = { "PATH_INFO" => "value" }
838
- transaction.set_headers(headers)
839
- transaction.set_headers_if_nil(nil)
995
+ it "does not update the headers on the transaction if the given value is nil" do
996
+ headers = { "PATH_INFO" => "value" }
997
+ transaction.add_headers(headers)
998
+ transaction.add_headers_if_nil(nil)
840
999
 
841
- transaction._sample
842
- expect(transaction).to include_environment(headers)
843
- end
1000
+ transaction._sample
1001
+ expect(transaction).to include_environment(headers)
844
1002
  end
845
1003
  end
846
1004
 
847
- context "when the params are set" do
848
- it "does not update the params on the transaction" do
1005
+ context "when the headers are set" do
1006
+ it "does not update the headers on the transaction" do
849
1007
  preset_headers = { "PATH_INFO" => "/first-path" }
850
1008
  headers = { "PATH_INFO" => "/other-path" }
851
- transaction.set_headers(preset_headers)
852
- transaction.set_headers_if_nil(headers)
1009
+ transaction.add_headers(preset_headers)
1010
+ transaction.add_headers_if_nil(headers)
853
1011
 
854
1012
  transaction._sample
855
1013
  expect(transaction).to include_environment(preset_headers)
856
1014
  end
857
1015
 
858
- it "does not update the params with a block on the transaction" do
1016
+ it "does not update the headers with a block on the transaction" do
859
1017
  preset_headers = { "PATH_INFO" => "/first-path" }
860
1018
  headers = { "PATH_INFO" => "/other-path" }
861
- transaction.set_headers(preset_headers)
862
- transaction.set_headers_if_nil { headers }
1019
+ transaction.add_headers(preset_headers)
1020
+ transaction.add_headers_if_nil { headers }
863
1021
 
864
1022
  transaction._sample
865
1023
  expect(transaction).to include_environment(preset_headers)
@@ -867,12 +1025,12 @@ describe Appsignal::Transaction do
867
1025
  end
868
1026
  end
869
1027
 
870
- describe "#set_tags" do
1028
+ describe "#add_tags" do
871
1029
  let(:transaction) { new_transaction }
872
1030
  let(:long_string) { "a" * 10_001 }
873
1031
 
874
1032
  it "stores tags on the transaction" do
875
- transaction.set_tags(
1033
+ transaction.add_tags(
876
1034
  :valid_key => "valid_value",
877
1035
  "valid_string_key" => "valid_value",
878
1036
  :both_symbols => :valid_value,
@@ -900,8 +1058,8 @@ describe Appsignal::Transaction do
900
1058
  end
901
1059
 
902
1060
  it "merges the tags when called multiple times" do
903
- transaction.set_tags(:key1 => "value1")
904
- transaction.set_tags(:key2 => "value2")
1061
+ transaction.add_tags(:key1 => "value1")
1062
+ transaction.add_tags(:key2 => "value2")
905
1063
  transaction._sample
906
1064
 
907
1065
  expect(transaction).to include_tags(
@@ -911,11 +1069,15 @@ describe Appsignal::Transaction do
911
1069
  end
912
1070
  end
913
1071
 
914
- describe "#set_custom_data" do
1072
+ describe "#add_custom_data" do
915
1073
  let(:transaction) { new_transaction }
916
1074
 
917
- it "stores custom Hash data on the transaction" do
918
- transaction.set_custom_data(
1075
+ it "has a #add_custom_data alias" do
1076
+ expect(transaction.method(:add_custom_data)).to eq(transaction.method(:set_custom_data))
1077
+ end
1078
+
1079
+ it "adds a custom Hash data to the transaction" do
1080
+ transaction.add_custom_data(
919
1081
  :user => {
920
1082
  :id => 123,
921
1083
  :locale => "abc"
@@ -939,8 +1101,8 @@ describe Appsignal::Transaction do
939
1101
  )
940
1102
  end
941
1103
 
942
- it "stores custom Array data on the transaction" do
943
- transaction.set_custom_data([
1104
+ it "adds a custom Array data to the transaction" do
1105
+ transaction.add_custom_data([
944
1106
  [123, "abc"],
945
1107
  ["appsignal", "enterprise"]
946
1108
  ])
@@ -955,39 +1117,42 @@ describe Appsignal::Transaction do
955
1117
  it "does not store non Hash or Array custom data" do
956
1118
  logs =
957
1119
  capture_logs do
958
- transaction.set_custom_data("abc")
1120
+ transaction.add_custom_data("abc")
959
1121
  transaction._sample
960
1122
  expect(transaction).to_not include_custom_data
961
1123
 
962
- transaction.set_custom_data(123)
1124
+ transaction.add_custom_data(123)
963
1125
  transaction._sample
964
1126
  expect(transaction).to_not include_custom_data
965
1127
 
966
- transaction.set_custom_data(Object.new)
1128
+ transaction.add_custom_data(Object.new)
967
1129
  transaction._sample
968
1130
  expect(transaction).to_not include_custom_data
969
1131
  end
970
1132
 
971
1133
  expect(logs).to contains_log(
972
1134
  :error,
973
- "set_custom_data: Unsupported data type String received."
1135
+ %(Sample data 'custom_data': Unsupported data type 'String' received: "abc")
974
1136
  )
975
1137
  expect(logs).to contains_log(
976
1138
  :error,
977
- "set_custom_data: Unsupported data type Integer received."
1139
+ %(Sample data 'custom_data': Unsupported data type 'Integer' received: 123)
978
1140
  )
979
1141
  expect(logs).to contains_log(
980
1142
  :error,
981
- "set_custom_data: Unsupported data type String received."
1143
+ %(Sample data 'custom_data': Unsupported data type 'Object' received: #<Object:)
982
1144
  )
983
1145
  end
984
1146
 
985
- it "overwrites the custom data if called multiple times" do
986
- transaction.set_custom_data("user" => { "id" => 123 })
987
- transaction.set_custom_data("user" => { "id" => 456 })
1147
+ it "merges the custom data if called multiple times" do
1148
+ transaction.add_custom_data("abc" => "value")
1149
+ transaction.add_custom_data("def" => "value")
988
1150
 
989
1151
  transaction._sample
990
- expect(transaction).to include_custom_data("user" => { "id" => 456 })
1152
+ expect(transaction).to include_custom_data(
1153
+ "abc" => "value",
1154
+ "def" => "value"
1155
+ )
991
1156
  end
992
1157
  end
993
1158
 
@@ -1150,42 +1315,6 @@ describe Appsignal::Transaction do
1150
1315
  end
1151
1316
  end
1152
1317
 
1153
- describe "#set_http_or_background_action" do
1154
- let(:transaction) { new_transaction }
1155
-
1156
- context "for a hash with controller and action" do
1157
- it "sets the action" do
1158
- transaction.set_http_or_background_action(
1159
- :controller => "HomeController",
1160
- :action => "show"
1161
- )
1162
- expect(transaction).to have_action("HomeController#show")
1163
- end
1164
- end
1165
-
1166
- context "for a hash with just action" do
1167
- it "sets the action" do
1168
- transaction.set_http_or_background_action(:action => "show")
1169
- expect(transaction).to have_action("show")
1170
- end
1171
- end
1172
-
1173
- context "for a hash with class and method" do
1174
- it "sets the action" do
1175
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1176
- expect(transaction).to have_action("Worker#perform")
1177
- end
1178
- end
1179
-
1180
- context "when action is already set" do
1181
- it "does not overwrite the set action" do
1182
- transaction.set_action("MyCustomAction#perform")
1183
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1184
- expect(transaction).to have_action("MyCustomAction#perform")
1185
- end
1186
- end
1187
- end
1188
-
1189
1318
  describe "#set_queue_start" do
1190
1319
  let(:transaction) { new_transaction }
1191
1320
 
@@ -1210,75 +1339,6 @@ describe Appsignal::Transaction do
1210
1339
  end
1211
1340
  end
1212
1341
 
1213
- describe "#set_http_or_background_queue_start" do
1214
- let(:transaction) { legacy_new_transaction(:request => legacy_request(env)) }
1215
- let(:err_stream) { std_stream }
1216
- let(:stderr) { err_stream.read }
1217
- let(:header_factor) { 1_000 }
1218
- let(:env_queue_start) { fixed_time + 20 } # in seconds
1219
-
1220
- def set_http_or_background_queue_start
1221
- capture_std_streams(std_stream, err_stream) do
1222
- transaction.set_http_or_background_queue_start
1223
- end
1224
- end
1225
-
1226
- context "when a queue time is found in a request header" do
1227
- let(:header_time) { ((fixed_time + 10) * header_factor).to_i } # in milliseconds
1228
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time}" } }
1229
-
1230
- it "sets the http header value in milliseconds on the transaction" do
1231
- set_http_or_background_queue_start
1232
-
1233
- expect(transaction).to have_queue_start(1_389_783_610_000)
1234
- end
1235
-
1236
- it "logs a deprecation message" do
1237
- logs = capture_logs { set_http_or_background_queue_start }
1238
-
1239
- expect(logs).to contains_log(
1240
- :warn,
1241
- "The Appsignal::Transaction#set_http_or_background_queue_start " \
1242
- "method has been deprecated."
1243
- )
1244
- end
1245
-
1246
- it "prints a deprecation message" do
1247
- set_http_or_background_queue_start
1248
-
1249
- expect(stderr).to include(
1250
- "The Appsignal::Transaction#set_http_or_background_queue_start " \
1251
- "method has been deprecated."
1252
- )
1253
- end
1254
-
1255
- context "when a :queue_start key is found in the transaction environment" do
1256
- let(:env) do
1257
- {
1258
- "HTTP_X_REQUEST_START" => "t=#{header_time}",
1259
- :queue_start => env_queue_start
1260
- }
1261
- end
1262
-
1263
- it "sets the http header value in milliseconds on the transaction" do
1264
- set_http_or_background_queue_start
1265
-
1266
- expect(transaction).to have_queue_start(1_389_783_610_000)
1267
- end
1268
- end
1269
- end
1270
-
1271
- context "when a :queue_start key is found in the transaction environment" do
1272
- let(:env) { { :queue_start => env_queue_start } } # in seconds
1273
-
1274
- it "sets the :queue_start value in milliseconds on the transaction" do
1275
- set_http_or_background_queue_start
1276
-
1277
- expect(transaction).to have_queue_start(1_389_783_620_000)
1278
- end
1279
- end
1280
- end
1281
-
1282
1342
  describe "#set_metadata" do
1283
1343
  let(:transaction) { new_transaction }
1284
1344
 
@@ -1289,8 +1349,7 @@ describe Appsignal::Transaction do
1289
1349
  end
1290
1350
 
1291
1351
  context "when filter_metadata includes metadata key" do
1292
- before { Appsignal.config[:filter_metadata] = ["filter_key"] }
1293
- after { Appsignal.config[:filter_metadata] = [] }
1352
+ let(:options) { { :filter_metadata => ["filter_key"] } }
1294
1353
 
1295
1354
  it "does not set the metadata on the transaction" do
1296
1355
  transaction.set_metadata(:filter_key, "filtered value")
@@ -1363,14 +1422,22 @@ describe Appsignal::Transaction do
1363
1422
  expect(transaction).to_not include_params
1364
1423
  end
1365
1424
 
1366
- expect(logs).to contains_log :error,
1367
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"some string"')
1368
- expect(logs).to contains_log :error,
1369
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '123')
1370
- expect(logs).to contains_log :error,
1371
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Class>"')
1372
- expect(logs).to contains_log :error,
1373
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Set>"')
1425
+ expect(logs).to contains_log(
1426
+ :error,
1427
+ %(Sample data 'params': Unsupported data type 'String' received: "some string")
1428
+ )
1429
+ expect(logs).to contains_log(
1430
+ :error,
1431
+ %(Sample data 'params': Unsupported data type 'Integer' received: 123)
1432
+ )
1433
+ expect(logs).to contains_log(
1434
+ :error,
1435
+ %(Sample data 'params': Unsupported data type 'Class' received: #<Class)
1436
+ )
1437
+ expect(logs).to contains_log(
1438
+ :error,
1439
+ %(Sample data 'params': Unsupported data type 'Set' received: #<Set: {"some value"}>)
1440
+ )
1374
1441
  end
1375
1442
 
1376
1443
  it "does not store data that can't be converted to JSON" do
@@ -1400,7 +1467,8 @@ describe Appsignal::Transaction do
1400
1467
 
1401
1468
  it "updates the sample data on the transaction" do
1402
1469
  silence do
1403
- transaction.set_sample_data(
1470
+ transaction.send(
1471
+ :set_sample_data,
1404
1472
  "params",
1405
1473
  :controller => "blog_posts",
1406
1474
  :action => "show",
@@ -1419,7 +1487,7 @@ describe Appsignal::Transaction do
1419
1487
  it "does not update the sample data on the transaction" do
1420
1488
  logs =
1421
1489
  capture_logs do
1422
- silence { transaction.set_sample_data("params", "string") }
1490
+ silence { transaction.send(:set_sample_data, "params", "string") }
1423
1491
  end
1424
1492
 
1425
1493
  expect(transaction.to_h["sample_data"]).to eq({})
@@ -1437,7 +1505,7 @@ describe Appsignal::Transaction do
1437
1505
  end
1438
1506
  logs =
1439
1507
  capture_logs do
1440
- silence { transaction.set_sample_data("params", klass.new => 1) }
1508
+ silence { transaction.send(:set_sample_data, "params", klass.new => 1) }
1441
1509
  end
1442
1510
 
1443
1511
  expect(transaction).to_not include_params
@@ -1447,91 +1515,202 @@ describe Appsignal::Transaction do
1447
1515
  end
1448
1516
  end
1449
1517
 
1450
- describe "#sample_data" do
1451
- let(:transaction) { legacy_new_transaction(:request => rack_request(env)) }
1452
- let(:env) do
1453
- Rack::MockRequest.env_for(
1454
- "/blog",
1455
- "REQUEST_METHOD" => "GET",
1456
- "SERVER_NAME" => "example.org",
1457
- "SERVER_PORT" => "80",
1458
- "PATH_INFO" => "/blog",
1459
- "rack.session" => { "session" => "value" },
1460
- :params => {
1461
- "controller" => "blog_posts",
1462
- "action" => "show",
1463
- "id" => "1"
1464
- }
1465
- ).merge(
1466
- :metadata => { "metadata" => "value" }
1467
- )
1518
+ describe "#add_error" do
1519
+ let(:transaction) { create_transaction }
1520
+
1521
+ let(:error) do
1522
+ e = ExampleStandardError.new("test message")
1523
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1524
+ e
1468
1525
  end
1469
1526
 
1470
- it "sets sample data from request" do
1471
- transaction.set_tags "tag" => "value"
1472
- transaction.add_breadcrumb "category", "action", "message", "key" => "value"
1473
- silence { transaction.sample_data }
1527
+ context "when error argument is not an error" do
1528
+ let(:error) { Object.new }
1474
1529
 
1475
- expect(transaction).to include_environment(
1476
- "REQUEST_METHOD" => "GET",
1477
- "SERVER_NAME" => "example.org",
1478
- "SERVER_PORT" => "80",
1479
- "PATH_INFO" => "/blog"
1480
- )
1481
- expect(transaction).to include_session_data("session" => "value")
1482
- expect(transaction).to include_params(
1483
- "controller" => "blog_posts",
1484
- "action" => "show",
1485
- "id" => "1"
1486
- )
1487
- expect(transaction).to include_sample_metadata("metadata" => "value")
1488
- expect(transaction).to include_tags("tag" => "value")
1489
- expect(transaction).to include_breadcrumb(
1490
- "action",
1491
- "category",
1492
- "message",
1493
- { "key" => "value" },
1494
- kind_of(Integer)
1495
- )
1530
+ it "does not add the error" do
1531
+ logs = capture_logs { transaction.add_error(error) }
1532
+
1533
+ expect(transaction).to_not have_error
1534
+ expect(logs).to contains_log(
1535
+ :error,
1536
+ "Appsignal::Transaction#add_error: Cannot add error. " \
1537
+ "The given value is not an exception: #{error.inspect}"
1538
+ )
1539
+ end
1496
1540
  end
1497
- end
1498
1541
 
1499
- describe "#set_error" do
1500
- let(:transaction) { new_transaction }
1501
- let(:env) { http_request_env_with_data }
1502
- let(:error) { ExampleStandardError.new("test message") }
1542
+ context "when AppSignal is not active" do
1543
+ it "does not add the error" do
1544
+ allow(Appsignal).to receive(:active?).and_return(false)
1503
1545
 
1504
- it "responds to add_exception for backwards compatibility" do
1505
- expect(transaction).to respond_to(:add_exception)
1546
+ transaction.add_error(error)
1547
+
1548
+ expect(transaction).to_not have_error
1549
+ end
1506
1550
  end
1507
1551
 
1508
- it "does not add the error if not active" do
1509
- allow(Appsignal).to receive(:active?).and_return(false)
1552
+ context "when a block is given" do
1553
+ it "stores the block in the error blocks" do
1554
+ block = proc { "block" }
1510
1555
 
1511
- transaction.set_error(error)
1556
+ transaction.add_error(error, &block)
1512
1557
 
1513
- expect(transaction).to_not have_error
1558
+ expect(transaction.error_blocks).to eq({
1559
+ error => [block]
1560
+ })
1561
+ end
1514
1562
  end
1515
1563
 
1516
- context "when error argument is not an error" do
1517
- let(:error) { Object.new }
1564
+ context "when no error is set in the transaction" do
1565
+ it "sets the error on the transaction" do
1566
+ transaction.add_error(error)
1518
1567
 
1519
- it "does not add the error" do
1520
- logs = capture_logs { transaction.set_error(error) }
1568
+ expect(transaction).to have_error(
1569
+ "ExampleStandardError",
1570
+ "test message",
1571
+ ["line 1"]
1572
+ )
1573
+ end
1574
+
1575
+ it "does store the error in the errors" do
1576
+ transaction.add_error(error)
1577
+
1578
+ expect(transaction.error_blocks).to eq({ error => [] })
1579
+ end
1580
+ end
1581
+
1582
+ context "when an error is already set in the transaction" do
1583
+ let(:other_error) do
1584
+ e = ExampleStandardError.new("other test message")
1585
+ allow(e).to receive(:backtrace).and_return(["line 2"])
1586
+ e
1587
+ end
1588
+
1589
+ before { transaction.set_error(other_error) }
1590
+
1591
+ it "stores an error in the errors" do
1592
+ transaction.add_error(error)
1593
+
1594
+ expect(transaction.error_blocks).to eq({
1595
+ other_error => [],
1596
+ error => []
1597
+ })
1598
+ end
1599
+
1600
+ it "does not set the error on the extension" do
1601
+ transaction.add_error(error)
1602
+
1603
+ expect(transaction).to have_error(
1604
+ "ExampleStandardError",
1605
+ "other test message",
1606
+ ["line 2"]
1607
+ )
1608
+ end
1609
+ end
1610
+
1611
+ context "when the error has already been added" do
1612
+ before { transaction.add_error(error) }
1613
+
1614
+ it "does not add the error to the errors" do
1615
+ expect(transaction.error_blocks).to eq({ error => [] })
1616
+
1617
+ transaction.add_error(error)
1618
+
1619
+ expect(transaction.error_blocks).to eq({ error => [] })
1620
+ end
1621
+
1622
+ context "when a block is given" do
1623
+ it "adds the block to the error blocks" do
1624
+ block = proc { "block" }
1625
+
1626
+ transaction.add_error(error, &block)
1627
+
1628
+ expect(transaction.error_blocks).to eq({ error => [block] })
1629
+ end
1630
+ end
1631
+ end
1632
+
1633
+ context "when the errors is at the limit" do
1634
+ let(:seen_error) { ExampleStandardError.new("error 0") }
1635
+
1636
+ before do
1637
+ transaction.add_error(seen_error)
1638
+
1639
+ 9.times do |i|
1640
+ transaction.add_error(ExampleStandardError.new("error #{i}"))
1641
+ end
1642
+ end
1643
+
1644
+ it "does not add a new error to the errors" do
1645
+ expect(transaction).to have_error("ExampleStandardError", "error 0", [])
1646
+ expect(transaction.error_blocks.length).to eq(10)
1647
+ expected_error_blocks = transaction.error_blocks.dup
1648
+
1649
+ transaction.add_error(error)
1650
+
1651
+ expect(transaction).to have_error("ExampleStandardError", "error 0", [])
1652
+ expect(transaction.error_blocks).to eq(expected_error_blocks)
1653
+ end
1654
+
1655
+ it "logs a debug message" do
1656
+ logs = capture_logs { transaction.add_error(error) }
1521
1657
 
1522
- expect(transaction).to_not have_error
1523
1658
  expect(logs).to contains_log(
1524
- :error,
1525
- "Appsignal::Transaction#set_error: Cannot set error. " \
1526
- "The given value is not an exception: #{error.inspect}"
1659
+ :warn,
1660
+ "Appsignal::Transaction#add_error: Transaction has more than 10 distinct errors. " \
1661
+ "Only the first 10 distinct errors will be reported."
1527
1662
  )
1528
1663
  end
1664
+
1665
+ context "when the error has already been added" do
1666
+ it "does not add the error to the errors" do
1667
+ expect(transaction.error_blocks.length).to eq(10)
1668
+
1669
+ transaction.add_error(seen_error)
1670
+
1671
+ expect(transaction.error_blocks.length).to eq(10)
1672
+ end
1673
+
1674
+ it "does add the block to the error blocks" do
1675
+ block = proc { "block" }
1676
+
1677
+ transaction.add_error(seen_error, &block)
1678
+
1679
+ expect(transaction.error_blocks[seen_error]).to eq([block])
1680
+ end
1681
+
1682
+ it "does not log a debug message" do
1683
+ logs = capture_logs { transaction.add_error(seen_error) }
1684
+
1685
+ expect(logs).to_not contains_log(
1686
+ :warn,
1687
+ "Appsignal::Transaction#add_error: Transaction has more than 10 distinct errors. " \
1688
+ "Only the first 10 distinct errors will be reported."
1689
+ )
1690
+ end
1691
+ end
1692
+ end
1693
+ end
1694
+
1695
+ describe "#_set_error" do
1696
+ let(:transaction) { new_transaction }
1697
+ let(:env) { http_request_env_with_data }
1698
+ let(:error) { ExampleStandardError.new("test message") }
1699
+
1700
+ it "responds to add_exception for backwards compatibility" do
1701
+ expect(transaction).to respond_to(:add_exception)
1702
+ end
1703
+
1704
+ it "does not add the error to the errors" do
1705
+ transaction.send(:_set_error, error)
1706
+
1707
+ expect(transaction.error_blocks).to be_empty
1529
1708
  end
1530
1709
 
1531
1710
  context "for a http request" do
1532
1711
  it "sets an error on the transaction" do
1533
1712
  allow(error).to receive(:backtrace).and_return(["line 1"])
1534
- transaction.set_error(error)
1713
+ transaction.send(:_set_error, error)
1535
1714
 
1536
1715
  expect(transaction).to have_error(
1537
1716
  "ExampleStandardError",
@@ -1542,10 +1721,10 @@ describe Appsignal::Transaction do
1542
1721
  end
1543
1722
 
1544
1723
  context "when the error has no causes" do
1545
- it "does not set the causes information as sample data" do
1546
- transaction.set_error(error)
1724
+ it "should set an empty causes array as sample data" do
1725
+ transaction.send(:_set_error, error)
1547
1726
 
1548
- expect(transaction).to_not include_error_causes
1727
+ expect(transaction).to include_error_causes([])
1549
1728
  end
1550
1729
  end
1551
1730
 
@@ -1560,8 +1739,12 @@ describe Appsignal::Transaction do
1560
1739
  e
1561
1740
  end
1562
1741
 
1742
+ let(:error_without_cause) do
1743
+ ExampleStandardError.new("error without cause")
1744
+ end
1745
+
1563
1746
  it "sends the causes information as sample data" do
1564
- transaction.set_error(error)
1747
+ transaction.send(:_set_error, error)
1565
1748
 
1566
1749
  expect(transaction).to have_error(
1567
1750
  "ExampleStandardError",
@@ -1581,6 +1764,19 @@ describe Appsignal::Transaction do
1581
1764
  ]
1582
1765
  )
1583
1766
  end
1767
+
1768
+ it "does not keep error causes from previously set errors" do
1769
+ transaction.send(:_set_error, error)
1770
+ transaction.send(:_set_error, error_without_cause)
1771
+
1772
+ expect(transaction).to have_error(
1773
+ "ExampleStandardError",
1774
+ "error without cause",
1775
+ []
1776
+ )
1777
+
1778
+ expect(transaction).to include_error_causes([])
1779
+ end
1584
1780
  end
1585
1781
 
1586
1782
  context "when the error has too many causes" do
@@ -1607,7 +1803,7 @@ describe Appsignal::Transaction do
1607
1803
  end
1608
1804
  expected_error_causes.last["is_root_cause"] = false
1609
1805
 
1610
- logs = capture_logs { transaction.set_error(error) }
1806
+ logs = capture_logs { transaction.send(:_set_error, error) }
1611
1807
 
1612
1808
  expect(transaction).to have_error(
1613
1809
  "ExampleStandardError",
@@ -1617,7 +1813,7 @@ describe Appsignal::Transaction do
1617
1813
  expect(transaction).to include_error_causes(expected_error_causes)
1618
1814
  expect(logs).to contains_log(
1619
1815
  :debug,
1620
- "Appsignal::Transaction#set_error: Error has more " \
1816
+ "Appsignal::Transaction#add_error: Error has more " \
1621
1817
  "than 10 error causes. Only the first 10 " \
1622
1818
  "will be reported."
1623
1819
  )
@@ -1633,11 +1829,11 @@ describe Appsignal::Transaction do
1633
1829
  end
1634
1830
 
1635
1831
  it "does not raise an error" do
1636
- transaction.set_error(error)
1832
+ transaction.send(:_set_error, error)
1637
1833
  end
1638
1834
 
1639
1835
  it "sets an error on the transaction without an error message" do
1640
- transaction.set_error(error)
1836
+ transaction.send(:_set_error, error)
1641
1837
 
1642
1838
  expect(transaction).to have_error(
1643
1839
  "ExampleStandardError",
@@ -1780,433 +1976,8 @@ describe Appsignal::Transaction do
1780
1976
  end
1781
1977
  end
1782
1978
 
1783
- context "GenericRequest" do
1784
- let(:env) { {} }
1785
- subject { Appsignal::Transaction::GenericRequest.new(env) }
1786
-
1787
- it "prints a deprecation warning on use" do
1788
- err_stream = std_stream
1789
- capture_std_streams(std_stream, err_stream) { subject }
1790
-
1791
- expect(err_stream.read).to include(
1792
- "appsignal WARNING: The use of Appsignal::Transaction::GenericRequest is deprecated."
1793
- )
1794
- end
1795
-
1796
- it "logs a deprecation warning on use" do
1797
- logs = capture_logs { silence { subject } }
1798
-
1799
- expect(logs).to contains_log(
1800
- :warn,
1801
- "The use of Appsignal::Transaction::GenericRequest is deprecated."
1802
- )
1803
- end
1804
-
1805
- it "initializes with an empty env" do
1806
- expect(subject.env).to be_empty
1807
- end
1808
-
1809
- context "when given an env" do
1810
- let(:env) do
1811
- {
1812
- :params => { :id => 1 },
1813
- :queue_start => 10
1814
- }
1815
- end
1816
-
1817
- it "sets the given env" do
1818
- expect(subject.env).to eq env
1819
- end
1820
-
1821
- it "sets the params present in the env" do
1822
- expect(subject.params).to eq(:id => 1)
1823
- end
1824
- end
1825
- end
1826
-
1827
1979
  # private
1828
1980
 
1829
- describe "#background_queue_start" do
1830
- let(:transaction) { legacy_new_transaction(:request => request) }
1831
- let(:request) { rack_request(env) }
1832
- let(:env) { {} }
1833
- subject { transaction.send(:background_queue_start) }
1834
-
1835
- context "when request is nil" do
1836
- let(:request) { nil }
1837
-
1838
- it { is_expected.to eq nil }
1839
- end
1840
-
1841
- context "when env is nil" do
1842
- before { expect(request).to receive(:env).and_return(nil) }
1843
-
1844
- it { is_expected.to eq nil }
1845
- end
1846
-
1847
- context "when queue start is nil" do
1848
- it { is_expected.to eq nil }
1849
- end
1850
-
1851
- context "when queue start is set" do
1852
- before do
1853
- env[:queue_start] = fixed_time
1854
- end
1855
-
1856
- it { is_expected.to eq 1_389_783_600_000 }
1857
- end
1858
- end
1859
-
1860
- describe "#http_queue_start" do
1861
- let(:transaction) { legacy_new_transaction(:request => request) }
1862
- let(:request) { rack_request(env) }
1863
- let(:env) { {} }
1864
- let(:slightly_earlier_time) { fixed_time - 0.4 }
1865
- let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
1866
- subject { transaction.send(:http_queue_start) }
1867
-
1868
- shared_examples "http queue start" do
1869
- context "when request is nil" do
1870
- let(:request) { nil }
1871
-
1872
- it { is_expected.to be_nil }
1873
- end
1874
-
1875
- context "when env is nil" do
1876
- before { expect(request).to receive(:env).and_return(nil) }
1877
-
1878
- it { is_expected.to be_nil }
1879
- end
1880
-
1881
- context "with no relevant header set" do
1882
- let(:env) { {} }
1883
-
1884
- it { is_expected.to be_nil }
1885
- end
1886
-
1887
- context "with the HTTP_X_REQUEST_START header set" do
1888
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}" } }
1889
-
1890
- it { is_expected.to eq 1_389_783_599_600 }
1891
-
1892
- context "with unparsable content" do
1893
- let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
1894
-
1895
- it { is_expected.to be_nil }
1896
- end
1897
-
1898
- context "with unparsable content at the end" do
1899
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}aaaa" } }
1900
-
1901
- it { is_expected.to eq 1_389_783_599_600 }
1902
- end
1903
-
1904
- context "with a really low number" do
1905
- let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
1906
-
1907
- it { is_expected.to be_nil }
1908
- end
1909
-
1910
- context "with the alternate HTTP_X_QUEUE_START header set" do
1911
- let(:env) { { "HTTP_X_QUEUE_START" => "t=#{slightly_earlier_time_value}" } }
1912
-
1913
- it { is_expected.to eq 1_389_783_599_600 }
1914
- end
1915
- end
1916
- end
1917
-
1918
- context "time in milliseconds" do
1919
- let(:factor) { 1_000 }
1920
-
1921
- it_should_behave_like "http queue start"
1922
- end
1923
-
1924
- context "time in microseconds" do
1925
- let(:factor) { 1_000_000 }
1926
-
1927
- it_should_behave_like "http queue start"
1928
- end
1929
- end
1930
-
1931
- describe "#sanitized_params" do
1932
- let(:transaction) { new_transaction }
1933
- subject { transaction.send(:sanitized_params) }
1934
-
1935
- context "with params" do
1936
- before do
1937
- transaction.set_params(:foo => "bar", :baz => :bat)
1938
- end
1939
-
1940
- it "returns params" do
1941
- is_expected.to eq(:foo => "bar", :baz => :bat)
1942
- end
1943
-
1944
- context "with AppSignal filtering" do
1945
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1946
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
1947
-
1948
- it "returns sanitized custom params" do
1949
- expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
1950
- end
1951
- end
1952
- end
1953
-
1954
- context "params from request" do
1955
- let(:transaction) { legacy_new_transaction(:request => request, :options => options) }
1956
- let(:options) { {} }
1957
- let(:request) { rack_request(env) }
1958
- let(:env) { {} }
1959
-
1960
- context "without request params" do
1961
- before { allow(transaction.request).to receive(:params).and_return(nil) }
1962
-
1963
- it { is_expected.to be_nil }
1964
- end
1965
-
1966
- context "when request params crashes" do
1967
- before { expect(request).to receive(:params).and_raise(NoMethodError) }
1968
-
1969
- it { is_expected.to be_nil }
1970
- end
1971
-
1972
- context "when request params method does not exist" do
1973
- let(:options) { { :params_method => :nonsense } }
1974
-
1975
- it { is_expected.to be_nil }
1976
- end
1977
-
1978
- context "when not sending params" do
1979
- before { Appsignal.config.config_hash[:send_params] = false }
1980
- after { Appsignal.config.config_hash[:send_params] = true }
1981
-
1982
- it { is_expected.to be_nil }
1983
- end
1984
-
1985
- context "with an array" do
1986
- let(:request) { legacy_request(:params => %w[arg1 arg2]) }
1987
-
1988
- it { is_expected.to eq %w[arg1 arg2] }
1989
-
1990
- context "with AppSignal filtering" do
1991
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1992
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
1993
-
1994
- it { is_expected.to eq %w[arg1 arg2] }
1995
- end
1996
- end
1997
-
1998
- context "with env" do
1999
- context "with sanitization" do
2000
- let(:request) { legacy_request(:params => { :foo => :bar }) }
2001
-
2002
- it "should call the params sanitizer" do
2003
- expect(subject).to eq(:foo => :bar)
2004
- end
2005
- end
2006
-
2007
- context "with AppSignal filtering" do
2008
- let(:request) { legacy_request(:params => { :foo => :bar, :baz => :bat }) }
2009
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
2010
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
2011
-
2012
- it "should call the params sanitizer with filtering" do
2013
- expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
2014
- end
2015
- end
2016
- end
2017
- end
2018
- end
2019
-
2020
- describe "#sanitized_environment" do
2021
- let(:transaction) { legacy_new_transaction(:request => request) }
2022
- let(:request) { rack_request(env) }
2023
- let(:env) { {} }
2024
- let(:allowlisted_keys) { Appsignal.config[:request_headers] }
2025
- subject { transaction.send(:sanitized_environment) }
2026
-
2027
- context "when request is nil" do
2028
- let(:request) { nil }
2029
-
2030
- it { is_expected.to be_nil }
2031
- end
2032
-
2033
- context "when env is nil" do
2034
- before { expect(request).to receive(:env).and_return(nil) }
2035
-
2036
- it { is_expected.to be_nil }
2037
- end
2038
-
2039
- context "when env is present" do
2040
- let(:env) do
2041
- {}.tap do |hash|
2042
- allowlisted_keys.each { |o| hash[o] = 1 } # use all allowlisted keys
2043
- hash[allowlisted_keys] = nil # don't add if nil
2044
- hash[:not_allowlisted] = "I will be sanitized"
2045
- end
2046
- end
2047
-
2048
- it "only sets allowlisted keys" do
2049
- expect(subject.keys).to match_array(allowlisted_keys)
2050
- end
2051
-
2052
- context "with configured request_headers" do
2053
- before do
2054
- Appsignal.config.config_hash[:request_headers] = %w[CONTENT_LENGTH]
2055
- end
2056
-
2057
- it "only sets allowlisted keys" do
2058
- expect(subject.keys).to match_array(%w[CONTENT_LENGTH])
2059
- end
2060
- end
2061
- end
2062
- end
2063
-
2064
- describe "#sanitized_session_data" do
2065
- let(:transaction) { legacy_new_transaction(:request => request) }
2066
- let(:request) { rack_request(env) }
2067
- let(:env) { {} }
2068
- subject { transaction.send(:sanitized_session_data) }
2069
-
2070
- context "when request is nil" do
2071
- let(:request) { nil }
2072
-
2073
- it { is_expected.to be_nil }
2074
- end
2075
-
2076
- context "when session is nil" do
2077
- before { expect(transaction.request).to receive(:session).and_return(nil) }
2078
-
2079
- it { is_expected.to be_nil }
2080
- end
2081
-
2082
- context "when session is empty" do
2083
- before { expect(transaction.request).to receive(:session).and_return({}) }
2084
-
2085
- it { is_expected.to eq({}) }
2086
- end
2087
-
2088
- context "when request class does not have a session method" do
2089
- let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
2090
-
2091
- it { is_expected.to be_nil }
2092
- end
2093
-
2094
- context "with a session" do
2095
- let(:session_data_filter) { [] }
2096
- before { Appsignal.config[:filter_session_data] = session_data_filter }
2097
- after { Appsignal.config[:filter_session_data] = [] }
2098
-
2099
- context "with generic session object" do
2100
- before do
2101
- expect(transaction).to respond_to(:request)
2102
- allow(transaction).to receive_message_chain(
2103
- :request,
2104
- :session => { :foo => :bar, :abc => :def }
2105
- )
2106
- allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
2107
- end
2108
-
2109
- context "without session filtering" do
2110
- it "keeps the session data intact" do
2111
- expect(subject).to eq(:foo => :bar, :abc => :def)
2112
- end
2113
- end
2114
-
2115
- context "with session filtering" do
2116
- let(:session_data_filter) { %w[foo] }
2117
-
2118
- it "filters the session data" do
2119
- expect(subject).to eq(:foo => "[FILTERED]", :abc => :def)
2120
- end
2121
- end
2122
- end
2123
-
2124
- if defined? ActionDispatch::Request::Session
2125
- context "with ActionDispatch::Request::Session" do
2126
- let(:action_dispatch_session) do
2127
- store = Class.new do
2128
- def load_session(_env)
2129
- [1, { :foo => :bar, :abc => :def }]
2130
- end
2131
-
2132
- def session_exists?(_env)
2133
- true
2134
- end
2135
- end.new
2136
- ActionDispatch::Request::Session.create(store,
2137
- ActionDispatch::Request.new("rack.input" => StringIO.new), {})
2138
- end
2139
- before do
2140
- expect(transaction).to respond_to(:request)
2141
- allow(transaction).to receive_message_chain(
2142
- :request,
2143
- :session => action_dispatch_session
2144
- )
2145
- allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
2146
- end
2147
-
2148
- context "without session filtering" do
2149
- it "keeps the session data intact" do
2150
- expect(subject).to eq("foo" => :bar, "abc" => :def)
2151
- end
2152
- end
2153
-
2154
- context "with session filtering" do
2155
- let(:session_data_filter) { %w[foo] }
2156
-
2157
- it "filters the session data" do
2158
- expect(subject).to eq("foo" => "[FILTERED]", "abc" => :def)
2159
- end
2160
- end
2161
- end
2162
- end
2163
-
2164
- context "when not sending session data" do
2165
- before { Appsignal.config[:send_session_data] = false }
2166
-
2167
- it "does not set any session data on the transaction" do
2168
- expect(subject).to be_nil
2169
- end
2170
- end
2171
- end
2172
- end
2173
-
2174
- describe "#sanitized_metadata" do
2175
- let(:transaction) { legacy_new_transaction(:request => request) }
2176
- let(:request) { rack_request(env) }
2177
- let(:env) { {} }
2178
- subject { transaction.send(:sanitized_metadata) }
2179
-
2180
- context "when request is nil" do
2181
- let(:request) { nil }
2182
-
2183
- it { is_expected.to be_nil }
2184
- end
2185
-
2186
- context "when env is nil" do
2187
- before { expect(request).to receive(:env).and_return(nil) }
2188
-
2189
- it { is_expected.to be_nil }
2190
- end
2191
-
2192
- context "when env is present" do
2193
- let(:env) { { :metadata => { "key" => "value" } } }
2194
-
2195
- it do
2196
- is_expected.to eq("key" => "value")
2197
- end
2198
-
2199
- context "with filter_metadata option set" do
2200
- before { Appsignal.config[:filter_metadata] = ["key"] }
2201
- after { Appsignal.config[:filter_metadata] = [] }
2202
-
2203
- it "filters out keys listed in the filter_metadata option" do
2204
- expect(subject.keys).to_not include("key")
2205
- end
2206
- end
2207
- end
2208
- end
2209
-
2210
1981
  describe "#cleaned_backtrace" do
2211
1982
  let(:transaction) { new_transaction }
2212
1983
  subject { transaction.send(:cleaned_backtrace, ["line 1", "line 2"]) }
@@ -2313,13 +2084,13 @@ describe Appsignal::Transaction do
2313
2084
  describe Appsignal::Transaction::NilTransaction do
2314
2085
  subject { Appsignal::Transaction::NilTransaction.new }
2315
2086
 
2316
- it "should have method stubs" do
2087
+ it "has method stubs" do
2317
2088
  subject.complete
2318
2089
  subject.pause!
2319
2090
  subject.resume!
2320
2091
  subject.paused?
2321
2092
  subject.store(:key)
2322
- subject.set_tags(:tag => 1)
2093
+ subject.add_tags(:tag => 1)
2323
2094
  subject.set_action("action")
2324
2095
  subject.set_http_or_background_action
2325
2096
  subject.set_queue_start(1)