appsignal 2.8.4.beta.1 → 2.9.18.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  3. data/.github/ISSUE_TEMPLATE/chore.md +14 -0
  4. data/.gitignore +2 -3
  5. data/.rubocop.yml +3 -0
  6. data/.rubocop_todo.yml +7 -16
  7. data/.travis.yml +28 -27
  8. data/CHANGELOG.md +657 -533
  9. data/README.md +31 -3
  10. data/Rakefile +128 -129
  11. data/SUPPORT.md +16 -0
  12. data/appsignal.gemspec +17 -4
  13. data/build_matrix.yml +21 -9
  14. data/ext/Rakefile +23 -17
  15. data/ext/agent.yml +40 -37
  16. data/ext/base.rb +116 -31
  17. data/ext/extconf.rb +34 -28
  18. data/gemfiles/capistrano2.gemfile +5 -0
  19. data/gemfiles/capistrano3.gemfile +5 -0
  20. data/gemfiles/grape.gemfile +5 -0
  21. data/gemfiles/no_dependencies.gemfile +5 -0
  22. data/gemfiles/padrino.gemfile +5 -0
  23. data/gemfiles/que.gemfile +5 -0
  24. data/gemfiles/que_beta.gemfile +10 -0
  25. data/gemfiles/rails-3.2.gemfile +5 -0
  26. data/gemfiles/rails-4.0.gemfile +5 -0
  27. data/gemfiles/rails-4.1.gemfile +5 -0
  28. data/gemfiles/rails-4.2.gemfile +5 -0
  29. data/gemfiles/rails-6.0.gemfile +5 -0
  30. data/gemfiles/resque.gemfile +5 -0
  31. data/lib/appsignal.rb +14 -492
  32. data/lib/appsignal/cli/demo.rb +5 -2
  33. data/lib/appsignal/cli/diagnose.rb +84 -4
  34. data/lib/appsignal/cli/diagnose/paths.rb +0 -5
  35. data/lib/appsignal/cli/diagnose/utils.rb +19 -0
  36. data/lib/appsignal/cli/helpers.rb +6 -0
  37. data/lib/appsignal/cli/install.rb +45 -15
  38. data/lib/appsignal/cli/notify_of_deploy.rb +10 -0
  39. data/lib/appsignal/config.rb +1 -2
  40. data/lib/appsignal/event_formatter.rb +4 -5
  41. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +10 -8
  42. data/lib/appsignal/event_formatter/moped/query_formatter.rb +60 -59
  43. data/lib/appsignal/extension.rb +2 -2
  44. data/lib/appsignal/helpers/instrumentation.rb +494 -0
  45. data/lib/appsignal/helpers/metrics.rb +54 -0
  46. data/lib/appsignal/hooks.rb +11 -8
  47. data/lib/appsignal/hooks/active_support_notifications.rb +2 -5
  48. data/lib/appsignal/hooks/puma.rb +74 -11
  49. data/lib/appsignal/hooks/sequel.rb +1 -1
  50. data/lib/appsignal/hooks/sidekiq.rb +115 -0
  51. data/lib/appsignal/integrations/mongo_ruby_driver.rb +7 -0
  52. data/lib/appsignal/integrations/que.rb +9 -8
  53. data/lib/appsignal/integrations/railtie.rb +2 -1
  54. data/lib/appsignal/marker.rb +2 -3
  55. data/lib/appsignal/minutely.rb +188 -19
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -1
  57. data/lib/appsignal/system.rb +16 -18
  58. data/lib/appsignal/transaction.rb +8 -0
  59. data/lib/appsignal/utils/rails_helper.rb +20 -0
  60. data/lib/appsignal/version.rb +1 -1
  61. data/lib/puma/plugin/appsignal.rb +26 -0
  62. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +40 -0
  63. data/spec/lib/appsignal/cli/diagnose_spec.rb +129 -22
  64. data/spec/lib/appsignal/cli/install_spec.rb +57 -8
  65. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +10 -0
  66. data/spec/lib/appsignal/config_spec.rb +13 -11
  67. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +38 -28
  68. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +6 -0
  69. data/spec/lib/appsignal/event_formatter_spec.rb +168 -69
  70. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +104 -25
  71. data/spec/lib/appsignal/hooks/puma_spec.rb +251 -34
  72. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +209 -0
  73. data/spec/lib/appsignal/hooks_spec.rb +4 -0
  74. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +24 -1
  75. data/spec/lib/appsignal/minutely_spec.rb +318 -26
  76. data/spec/lib/appsignal/system_spec.rb +0 -35
  77. data/spec/lib/appsignal/transaction_spec.rb +68 -10
  78. data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +39 -31
  79. data/spec/lib/appsignal/utils/json_spec.rb +7 -3
  80. data/spec/lib/appsignal_spec.rb +98 -22
  81. data/spec/lib/puma/appsignal_spec.rb +91 -0
  82. data/spec/spec_helper.rb +13 -0
  83. data/spec/support/{project_fixture → fixtures/projects/valid}/config/application.rb +0 -0
  84. data/spec/support/{project_fixture → fixtures/projects/valid}/config/appsignal.yml +1 -0
  85. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/development.rb +0 -0
  86. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/production.rb +0 -0
  87. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/test.rb +0 -0
  88. data/spec/support/{project_fixture → fixtures/projects/valid}/log/.gitkeep +0 -0
  89. data/spec/support/helpers/config_helpers.rb +1 -1
  90. data/spec/support/helpers/log_helpers.rb +6 -0
  91. data/spec/support/helpers/wait_for_helper.rb +28 -0
  92. data/spec/support/mocks/mock_probe.rb +11 -0
  93. data/spec/support/stubs/sidekiq/api.rb +4 -0
  94. metadata +43 -31
  95. data/spec/support/fixtures/containers/cgroups/docker +0 -14
  96. data/spec/support/fixtures/containers/cgroups/docker_systemd +0 -8
  97. data/spec/support/fixtures/containers/cgroups/lxc +0 -10
  98. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  99. data/spec/support/fixtures/containers/cgroups/none +0 -1
@@ -19,41 +19,6 @@ describe Appsignal::System do
19
19
  end
20
20
  end
21
21
 
22
- describe ".installed_agent_architecture" do
23
- let(:const_name) { "GEM_EXT_PATH".freeze }
24
- let(:tmp_ext_dir) { File.join(tmp_dir, "ext") }
25
- let(:architecture_file) { File.join(Appsignal::System::GEM_EXT_PATH, "appsignal.architecture") }
26
- around do |example|
27
- original_gem_ext_path = Appsignal::System.const_get(const_name)
28
- Appsignal::System.send(:remove_const, const_name)
29
- Appsignal::System.const_set(const_name, tmp_ext_dir)
30
- example.run
31
- Appsignal::System.send(:remove_const, const_name)
32
- Appsignal::System.const_set(const_name, original_gem_ext_path)
33
- end
34
- after { FileUtils.rm_rf(tmp_ext_dir) }
35
- subject { described_class.installed_agent_architecture }
36
-
37
- context "with an ext/appsignal.architecture file" do
38
- before do
39
- FileUtils.mkdir_p(Appsignal::System::GEM_EXT_PATH)
40
- File.open(architecture_file, "w") do |file|
41
- file.write "foo"
42
- end
43
- end
44
-
45
- it "returns the contents of the file" do
46
- expect(subject).to eq("foo")
47
- end
48
- end
49
-
50
- context "without an ext/appsignal.architecture file" do
51
- it "returns nil" do
52
- expect(subject).to be_nil
53
- end
54
- end
55
- end
56
-
57
22
  describe ".agent_platform" do
58
23
  let(:os) { "linux-gnu" }
59
24
  let(:ldd_output) { "" }
@@ -214,7 +214,7 @@ describe Appsignal::Transaction do
214
214
 
215
215
  context "pausing" do
216
216
  describe "#pause!" do
217
- it "should change the pause flag to true" do
217
+ it "changes the pause flag to true" do
218
218
  expect do
219
219
  transaction.pause!
220
220
  end.to change(transaction, :paused).from(false).to(true)
@@ -224,7 +224,7 @@ describe Appsignal::Transaction do
224
224
  describe "#resume!" do
225
225
  before { transaction.pause! }
226
226
 
227
- it "should change the pause flag to false" do
227
+ it "changes the pause flag to false" do
228
228
  expect do
229
229
  transaction.resume!
230
230
  end.to change(transaction, :paused).from(true).to(false)
@@ -232,14 +232,16 @@ describe Appsignal::Transaction do
232
232
  end
233
233
 
234
234
  describe "#paused?" do
235
- it "should return the pause state" do
236
- expect(transaction.paused?).to be_falsy
235
+ context "when not paused" do
236
+ it "return false" do
237
+ expect(transaction.paused?).to be_falsy
238
+ end
237
239
  end
238
240
 
239
241
  context "when paused" do
240
242
  before { transaction.pause! }
241
243
 
242
- it "should return the pause state" do
244
+ it "returns true" do
243
245
  expect(transaction.paused?).to be_truthy
244
246
  end
245
247
  end
@@ -628,7 +630,11 @@ describe Appsignal::Transaction do
628
630
 
629
631
  describe "#set_error" do
630
632
  let(:env) { http_request_env_with_data }
631
- let(:error) { double(:error, :message => "test message", :backtrace => ["line 1"]) }
633
+ let(:error) do
634
+ e = ExampleStandardError.new("test message")
635
+ allow(e).to receive(:backtrace).and_return(["line 1"])
636
+ e
637
+ end
632
638
 
633
639
  it "should also respond to add_exception for backwords compatibility" do
634
640
  expect(transaction).to respond_to(:add_exception)
@@ -641,10 +647,24 @@ describe Appsignal::Transaction do
641
647
  transaction.set_error(error)
642
648
  end
643
649
 
650
+ context "when error is not an error" do
651
+ let(:error) { Object.new }
652
+
653
+ it "does not add the error" do
654
+ expect(Appsignal.logger).to receive(:error).with(
655
+ "Appsignal::Transaction#set_error: Cannot set error. " \
656
+ "The given value is not an exception: #{error.inspect}"
657
+ )
658
+ expect(transaction.ext).to_not receive(:set_error)
659
+
660
+ transaction.set_error(error)
661
+ end
662
+ end
663
+
644
664
  context "for a http request" do
645
665
  it "should set an error in the extension" do
646
666
  expect(transaction.ext).to receive(:set_error).with(
647
- "RSpec::Mocks::Double",
667
+ "ExampleStandardError",
648
668
  "test message",
649
669
  Appsignal::Utils::Data.generate(["line 1"])
650
670
  )
@@ -654,7 +674,12 @@ describe Appsignal::Transaction do
654
674
  end
655
675
 
656
676
  context "when error message is nil" do
657
- let(:error) { double(:error, :message => nil, :backtrace => ["line 1"]) }
677
+ let(:error) do
678
+ e = ExampleStandardError.new
679
+ allow(e).to receive(:message).and_return(nil)
680
+ allow(e).to receive(:backtrace).and_return(["line 1"])
681
+ e
682
+ end
658
683
 
659
684
  it "should not raise an error" do
660
685
  expect { transaction.set_error(error) }.to_not raise_error
@@ -662,7 +687,7 @@ describe Appsignal::Transaction do
662
687
 
663
688
  it "should set an error in the extension" do
664
689
  expect(transaction.ext).to receive(:set_error).with(
665
- "RSpec::Mocks::Double",
690
+ "ExampleStandardError",
666
691
  "",
667
692
  Appsignal::Utils::Data.generate(["line 1"])
668
693
  )
@@ -690,11 +715,20 @@ describe Appsignal::Transaction do
690
715
  end
691
716
 
692
717
  describe "#start_event" do
693
- it "should start the event in the extension" do
718
+ it "starts the event in the extension" do
694
719
  expect(transaction.ext).to receive(:start_event).with(0).and_call_original
695
720
 
696
721
  transaction.start_event
697
722
  end
723
+
724
+ context "when transaction is paused" do
725
+ it "does not start the event" do
726
+ transaction.pause!
727
+ expect(transaction.ext).to_not receive(:start_event)
728
+
729
+ transaction.start_event
730
+ end
731
+ end
698
732
  end
699
733
 
700
734
  describe "#finish_event" do
@@ -737,6 +771,15 @@ describe Appsignal::Transaction do
737
771
  nil
738
772
  )
739
773
  end
774
+
775
+ context "when transaction is paused" do
776
+ it "does not finish the event" do
777
+ transaction.pause!
778
+ expect(transaction.ext).to_not receive(:finish_event)
779
+
780
+ transaction.start_event
781
+ end
782
+ end
740
783
  end
741
784
 
742
785
  describe "#record_event" do
@@ -783,6 +826,21 @@ describe Appsignal::Transaction do
783
826
  nil
784
827
  )
785
828
  end
829
+
830
+ context "when transaction is paused" do
831
+ it "does not record the event" do
832
+ transaction.pause!
833
+ expect(transaction.ext).to_not receive(:record_event)
834
+
835
+ transaction.record_event(
836
+ "name",
837
+ nil,
838
+ nil,
839
+ 1000,
840
+ nil
841
+ )
842
+ end
843
+ end
786
844
  end
787
845
 
788
846
  describe "#instrument" do
@@ -30,19 +30,20 @@ describe Appsignal::Utils::HashSanitizer do
30
30
  let(:sanitized_params) { described_class.sanitize(params) }
31
31
  subject { sanitized_params }
32
32
 
33
- it { is_expected.to be_instance_of Hash }
34
- it { expect(subject[:text]).to eq("string") }
35
- it { expect(subject["string"]).to eq("string key value") }
36
- it do
33
+ it "returns a sanitized Hash" do
34
+ expect(subject).to_not eq(params)
35
+ is_expected.to be_instance_of Hash
36
+ expect(subject[:text]).to eq("string")
37
+ expect(subject["string"]).to eq("string key value")
37
38
  expect(subject[:file]).to be_instance_of String
38
39
  expect(subject[:file]).to include "::UploadedFile"
40
+ expect(subject[:float]).to eq(0.0)
41
+ expect(subject[:bool_true]).to be(true)
42
+ expect(subject[:bool_false]).to be(false)
43
+ expect(subject[:nil]).to be_nil
44
+ expect(subject[:int]).to eq(1)
45
+ expect(subject[:int64]).to eq(1 << 64)
39
46
  end
40
- it { expect(subject[:float]).to eq(0.0) }
41
- it { expect(subject[:bool_true]).to be(true) }
42
- it { expect(subject[:bool_false]).to be(false) }
43
- it { expect(subject[:nil]).to be_nil }
44
- it { expect(subject[:int]).to eq(1) }
45
- it { expect(subject[:int64]).to eq(1 << 64) }
46
47
 
47
48
  it "does not change the original params" do
48
49
  subject
@@ -50,29 +51,34 @@ describe Appsignal::Utils::HashSanitizer do
50
51
  expect(params[:hash][:nested_array][2]).to eq(file)
51
52
  end
52
53
 
53
- describe ":hash" do
54
+ describe ":hash key" do
54
55
  subject { sanitized_params[:hash] }
55
56
 
56
- it { is_expected.to be_instance_of Hash }
57
- it { expect(subject[:nested_text]).to eq("string") }
57
+ it "returns a sanitized Hash" do
58
+ expect(subject).to_not eq(params[:hash])
59
+ is_expected.to be_instance_of Hash
60
+ expect(subject[:nested_text]).to eq("string")
61
+ end
58
62
 
59
- describe ":nested_array" do
63
+ describe ":nested_array key" do
60
64
  subject { sanitized_params[:hash][:nested_array] }
61
65
 
62
- it { is_expected.to be_instance_of Array }
63
- it { expect(subject[0]).to eq("something") }
64
- it { expect(subject[1]).to eq("else") }
65
- it do
66
+ it "returns a sanitized Array" do
67
+ expect(subject).to_not eq(params[:hash][:nested_array])
68
+ is_expected.to be_instance_of Array
69
+ expect(subject[0]).to eq("something")
70
+ expect(subject[1]).to eq("else")
66
71
  expect(subject[2]).to be_instance_of String
67
72
  expect(subject[2]).to include "::UploadedFile"
68
73
  end
69
74
 
70
- describe ":nested_hash" do
75
+ describe ":nested_hash key" do
71
76
  subject { sanitized_params[:hash][:nested_array][3] }
72
77
 
73
- it { is_expected.to be_instance_of Hash }
74
- it { expect(subject[:key]).to eq("value") }
75
- it do
78
+ it "returns a sanitized Hash" do
79
+ expect(subject).to_not eq(params[:hash][:nested_array][3])
80
+ is_expected.to be_instance_of Hash
81
+ expect(subject[:key]).to eq("value")
76
82
  expect(subject[:file]).to be_instance_of String
77
83
  expect(subject[:file]).to include "::UploadedFile"
78
84
  end
@@ -86,17 +92,19 @@ describe Appsignal::Utils::HashSanitizer do
86
92
  end
87
93
  subject { sanitized_params }
88
94
 
89
- it { expect(subject[:text]).to eq(described_class::FILTERED) }
90
- it { expect(subject[:hash]).to eq(described_class::FILTERED) }
91
- it do
95
+ it "returns a sanitized Hash with the given keys filtered out" do
96
+ expect(subject).to_not eq(params)
97
+ expect(subject[:text]).to eq(described_class::FILTERED)
98
+ expect(subject[:hash]).to eq(described_class::FILTERED)
99
+
92
100
  expect(subject[:file]).to be_instance_of String
93
101
  expect(subject[:file]).to include "::UploadedFile"
102
+ expect(subject[:float]).to eq(0.0)
103
+ expect(subject[:bool_true]).to be(true)
104
+ expect(subject[:bool_false]).to be(false)
105
+ expect(subject[:nil]).to be_nil
106
+ expect(subject[:int]).to eq(1)
94
107
  end
95
- it { expect(subject[:float]).to eq(0.0) }
96
- it { expect(subject[:bool_true]).to be(true) }
97
- it { expect(subject[:bool_false]).to be(false) }
98
- it { expect(subject[:nil]).to be_nil }
99
- it { expect(subject[:int]).to eq(1) }
100
108
 
101
109
  context "with strings as key filter values" do
102
110
  let(:sanitized_params) do
@@ -108,7 +116,7 @@ describe Appsignal::Utils::HashSanitizer do
108
116
  end
109
117
  end
110
118
 
111
- describe ":hash" do
119
+ describe ":hash key" do
112
120
  let(:sanitized_params) do
113
121
  described_class.sanitize(params, %w[nested_text])
114
122
  end
@@ -16,10 +16,12 @@ describe Appsignal::Utils::JSON do
16
16
  }
17
17
  end
18
18
 
19
- it { is_expected.to eq %({"the":"payload","1":true,"":"test","foo":[1,2,"three"],"bar":null,"baz":{"foo":"bar"}}) }
19
+ it "returns a JSON string" do
20
+ is_expected.to eq %({"the":"payload","1":true,"":"test","foo":[1,2,"three"],"bar":null,"baz":{"foo":"bar"}})
21
+ end
20
22
  end
21
23
 
22
- context "with a body that contains strings with invalid utf-8 content" do
24
+ context "with a body that contains strings with invalid UTF-8 content" do
23
25
  let(:string_with_invalid_utf8) { [0x61, 0x61, 0x85].pack("c*") }
24
26
  let(:body) do
25
27
  {
@@ -34,7 +36,9 @@ describe Appsignal::Utils::JSON do
34
36
  }
35
37
  end
36
38
 
37
- it { is_expected.to eq %({"field_one":"aa","field_two":"aa�","field_three":["one","aa�"],"field_four":{"one":"aa�"}}) }
39
+ it "returns a JSON string with invalid UTF-8 content" do
40
+ is_expected.to eq %({"field_one":"aa","field_two":"aa�","field_three":["one","aa�"],"field_four":{"one":"aa�"}})
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -90,14 +90,10 @@ describe Appsignal do
90
90
  unless Appsignal::System.jruby?
91
91
  it "installs the allocation event hook" do
92
92
  expect(Appsignal::Extension).to receive(:install_allocation_event_hook)
93
+ .and_call_original
93
94
  Appsignal.start
94
95
  end
95
96
  end
96
-
97
- it "should add the gc probe to minutely" do
98
- expect(Appsignal::Minutely).to receive(:add_gc_probe)
99
- Appsignal.start
100
- end
101
97
  end
102
98
 
103
99
  context "when allocation tracking and gc instrumentation have been disabled" do
@@ -117,7 +113,7 @@ describe Appsignal do
117
113
  end
118
114
 
119
115
  it "should not add the gc probe to minutely" do
120
- expect(Appsignal::Minutely).not_to receive(:add_gc_probe)
116
+ expect(Appsignal::Minutely).not_to receive(:register_garbage_collection_probe)
121
117
  Appsignal.start
122
118
  end
123
119
  end
@@ -244,6 +240,16 @@ describe Appsignal do
244
240
 
245
241
  context "not active" do
246
242
  describe ".monitor_transaction" do
243
+ let(:log_stream) { StringIO.new }
244
+ let(:log) { log_contents(log_stream) }
245
+ before do
246
+ Appsignal.config = project_fixture_config("not_active")
247
+ Appsignal.start
248
+ Appsignal.start_logger
249
+ Appsignal.logger = test_logger(log_stream)
250
+ end
251
+ after { Appsignal.logger = nil }
252
+
247
253
  it "should do nothing but still yield the block" do
248
254
  expect(Appsignal::Transaction).to_not receive(:create)
249
255
  expect(Appsignal).to_not receive(:instrument)
@@ -256,6 +262,23 @@ describe Appsignal do
256
262
  end).to eq 1
257
263
  end.to_not raise_error
258
264
  end
265
+
266
+ context "with an unknown event type" do
267
+ it "yields the given block" do
268
+ expect do |blk|
269
+ Appsignal.monitor_transaction("unknown.sidekiq", &blk)
270
+ end.to yield_control
271
+ end
272
+
273
+ it "logs an error" do
274
+ Appsignal.monitor_transaction("unknown.sidekiq") {}
275
+ expect(log).to contains_log(
276
+ :error,
277
+ "Unrecognized name 'unknown.sidekiq': names must start with either 'perform_job' " \
278
+ "(for jobs and tasks) or 'process_action' (for HTTP requests)"
279
+ )
280
+ end
281
+ end
259
282
  end
260
283
 
261
284
  describe ".listen_for_error" do
@@ -303,10 +326,15 @@ describe Appsignal do
303
326
  end
304
327
 
305
328
  context "with config and started" do
329
+ let(:log_stream) { StringIO.new }
330
+ let(:log) { log_contents(log_stream) }
306
331
  before do
307
332
  Appsignal.config = project_fixture_config
308
333
  Appsignal.start
334
+ Appsignal.start_logger
335
+ Appsignal.logger = test_logger(log_stream)
309
336
  end
337
+ after { Appsignal.logger = nil }
310
338
 
311
339
  describe ".monitor_transaction" do
312
340
  context "with a successful call" do
@@ -363,6 +391,23 @@ describe Appsignal do
363
391
  end.to raise_error(error)
364
392
  end
365
393
  end
394
+
395
+ context "with an unknown event type" do
396
+ it "yields the given block" do
397
+ expect do |blk|
398
+ Appsignal.monitor_transaction("unknown.sidekiq", &blk)
399
+ end.to yield_control
400
+ end
401
+
402
+ it "logs an error" do
403
+ Appsignal.monitor_transaction("unknown.sidekiq") {}
404
+ expect(log).to contains_log(
405
+ :error,
406
+ "Unrecognized name 'unknown.sidekiq': names must start with either 'perform_job' " \
407
+ "(for jobs and tasks) or 'process_action' (for HTTP requests)"
408
+ )
409
+ end
410
+ end
366
411
  end
367
412
 
368
413
  describe ".monitor_single_transaction" do
@@ -612,8 +657,10 @@ describe Appsignal do
612
657
  let(:error) { double }
613
658
 
614
659
  it "logs an error message" do
615
- expect(Appsignal.logger).to receive(:error)
616
- .with("Can't send error, given value is not an exception")
660
+ expect(Appsignal.logger).to receive(:error).with(
661
+ "Appsignal.send_error: Cannot send error. " \
662
+ "The given value is not an exception: #{error.inspect}"
663
+ )
617
664
  end
618
665
 
619
666
  it "does not send the error" do
@@ -650,6 +697,29 @@ describe Appsignal do
650
697
 
651
698
  after { Appsignal.send_error(error, nil, namespace) }
652
699
  end
700
+
701
+ context "when given a block" do
702
+ it "yields the transaction and allows additional metadata to be set" do
703
+ captured_transaction = nil
704
+ Appsignal.send_error(StandardError.new("my_error")) do |transaction|
705
+ captured_transaction = transaction
706
+ transaction.set_action("my_action")
707
+ transaction.set_namespace("my_namespace")
708
+
709
+ # Don't flush the transaction, so we can inspect it
710
+ expect(transaction).to receive(:complete)
711
+ end
712
+ expect(captured_transaction.to_h).to include(
713
+ "namespace" => "my_namespace",
714
+ "action" => "my_action",
715
+ "error" => {
716
+ "name" => "StandardError",
717
+ "message" => "my_error",
718
+ "backtrace" => kind_of(String) # TODO: should be Array
719
+ }
720
+ )
721
+ end
722
+ end
653
723
  end
654
724
 
655
725
  describe ".listen_for_error" do
@@ -710,13 +780,19 @@ describe Appsignal do
710
780
  Appsignal.set_error(error)
711
781
  end
712
782
 
713
- context "when the error is nil" do
714
- it "does nothing" do
783
+ context "when the error is not an Exception" do
784
+ let(:error) { Object.new }
785
+
786
+ it "logs an error" do
787
+ expect(Appsignal.logger).to receive(:error).with(
788
+ "Appsignal.set_error: Cannot set error. " \
789
+ "The given value is not an exception: #{error.inspect}"
790
+ )
715
791
  expect(transaction).to_not receive(:set_error)
716
792
  expect(transaction).to_not receive(:set_tags)
717
793
  expect(transaction).to_not receive(:set_namespace)
718
794
 
719
- Appsignal.set_error(nil)
795
+ Appsignal.set_error(error)
720
796
  end
721
797
  end
722
798
 
@@ -831,25 +907,25 @@ describe Appsignal do
831
907
  end
832
908
 
833
909
  describe ".without_instrumentation" do
834
- let(:transaction) { double }
910
+ let(:transaction) { http_request_transaction }
835
911
  before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
836
912
 
837
- it "should pause and unpause the transaction around the block" do
838
- expect(transaction).to receive(:pause!)
839
- expect(transaction).to receive(:resume!)
913
+ it "does not record events on the transaction" do
914
+ expect(transaction).to receive(:pause!).and_call_original
915
+ expect(transaction).to receive(:resume!).and_call_original
916
+ Appsignal.instrument("register.this.event") { :do_nothing }
917
+ Appsignal.without_instrumentation do
918
+ Appsignal.instrument("dont.register.this.event") { :do_nothing }
919
+ end
920
+ expect(transaction.to_h["events"].map { |e| e["name"] })
921
+ .to match_array("register.this.event")
840
922
  end
841
923
 
842
924
  context "without transaction" do
843
925
  let(:transaction) { nil }
844
926
 
845
927
  it "should not crash" do
846
- # just execute the after block
847
- end
848
- end
849
-
850
- after do
851
- Appsignal.without_instrumentation do
852
- # nothing
928
+ Appsignal.without_instrumentation { :do_nothing }
853
929
  end
854
930
  end
855
931
  end