appsignal 2.9.18.beta.1 → 2.10.6.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -6
  3. data/.semaphore/semaphore.yml +964 -0
  4. data/CHANGELOG.md +48 -1
  5. data/README.md +1 -1
  6. data/Rakefile +82 -23
  7. data/appsignal.gemspec +5 -2
  8. data/build_matrix.yml +100 -52
  9. data/ext/agent.yml +19 -19
  10. data/gemfiles/capistrano2.gemfile +0 -5
  11. data/gemfiles/capistrano3.gemfile +0 -5
  12. data/gemfiles/grape.gemfile +0 -5
  13. data/gemfiles/no_dependencies.gemfile +0 -5
  14. data/gemfiles/padrino.gemfile +0 -6
  15. data/gemfiles/que.gemfile +0 -5
  16. data/gemfiles/que_beta.gemfile +0 -5
  17. data/gemfiles/rails-3.2.gemfile +0 -5
  18. data/gemfiles/rails-4.0.gemfile +0 -5
  19. data/gemfiles/rails-4.1.gemfile +0 -5
  20. data/gemfiles/rails-4.2.gemfile +0 -5
  21. data/gemfiles/resque.gemfile +0 -6
  22. data/lib/appsignal.rb +10 -7
  23. data/lib/appsignal/cli.rb +9 -2
  24. data/lib/appsignal/cli/diagnose.rb +21 -20
  25. data/lib/appsignal/cli/helpers.rb +22 -10
  26. data/lib/appsignal/cli/install.rb +2 -1
  27. data/lib/appsignal/cli/notify_of_deploy.rb +1 -1
  28. data/lib/appsignal/config.rb +23 -9
  29. data/lib/appsignal/event_formatter.rb +5 -6
  30. data/lib/appsignal/hooks/net_http.rb +13 -8
  31. data/lib/appsignal/integrations/grape.rb +2 -1
  32. data/lib/appsignal/integrations/net_http.rb +16 -0
  33. data/lib/appsignal/logger.rb +22 -0
  34. data/lib/appsignal/minutely.rb +5 -6
  35. data/lib/appsignal/rack/js_exception_catcher.rb +5 -0
  36. data/lib/appsignal/system.rb +6 -0
  37. data/lib/appsignal/transaction.rb +8 -3
  38. data/lib/appsignal/transmitter.rb +0 -1
  39. data/lib/appsignal/utils/deprecation_message.rb +2 -2
  40. data/lib/appsignal/version.rb +1 -1
  41. data/resources/cacert.pem +1404 -1804
  42. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  43. data/spec/lib/appsignal/cli/diagnose_spec.rb +54 -11
  44. data/spec/lib/appsignal/cli/helpers_spec.rb +11 -3
  45. data/spec/lib/appsignal/cli/install_spec.rb +30 -1
  46. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +4 -2
  47. data/spec/lib/appsignal/config_spec.rb +85 -14
  48. data/spec/lib/appsignal/event_formatter_spec.rb +5 -4
  49. data/spec/lib/appsignal/hooks/action_cable_spec.rb +1 -5
  50. data/spec/lib/appsignal/hooks/rake_spec.rb +41 -39
  51. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -15
  52. data/spec/lib/appsignal/integrations/grape_spec.rb +10 -0
  53. data/spec/lib/appsignal/integrations/object_spec.rb +2 -2
  54. data/spec/lib/appsignal/integrations/que_spec.rb +26 -39
  55. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +108 -46
  56. data/spec/lib/appsignal/integrations/resque_spec.rb +40 -39
  57. data/spec/lib/appsignal/logger_spec.rb +25 -0
  58. data/spec/lib/appsignal/minutely_spec.rb +7 -7
  59. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +23 -5
  60. data/spec/lib/appsignal/system_spec.rb +36 -0
  61. data/spec/lib/appsignal/transaction_spec.rb +37 -26
  62. data/spec/lib/appsignal/transmitter_spec.rb +3 -1
  63. data/spec/lib/appsignal_spec.rb +13 -10
  64. data/spec/spec_helper.rb +26 -12
  65. data/spec/support/fixtures/projects/broken/config/appsignal.yml +1 -0
  66. data/spec/support/helpers/cli_helpers.rb +15 -1
  67. data/spec/support/helpers/log_helpers.rb +13 -6
  68. data/spec/support/helpers/transaction_helpers.rb +53 -0
  69. data/spec/support/helpers/wait_for_helper.rb +2 -2
  70. data/spec/support/matchers/be_completed.rb +5 -0
  71. data/spec/support/matchers/have_colorized_text.rb +28 -0
  72. data/spec/support/testing.rb +113 -0
  73. data/support/check_versions +22 -0
  74. data/support/install_deps +9 -4
  75. metadata +32 -19
  76. data/.travis.yml +0 -149
@@ -18,65 +18,66 @@ if DependencyHelper.resque_present?
18
18
  extend Appsignal::Integrations::ResquePlugin
19
19
 
20
20
  def self.perform
21
- raise ExampleException
21
+ raise ExampleException, "my error message"
22
22
  end
23
23
  end
24
24
  end
25
25
 
26
26
  describe :around_perform_resque_plugin do
27
- let(:transaction) { Appsignal::Transaction.new("1", "background", {}, {}) }
28
27
  let(:job) { ::Resque::Job.new("default", "class" => "TestJob") }
29
- before do
30
- allow(transaction).to receive(:complete).and_return(true)
31
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
32
- expect(Appsignal).to receive(:stop)
33
- end
28
+ before { expect(Appsignal).to receive(:stop) }
34
29
 
35
30
  context "without exception" do
36
31
  it "creates a new transaction" do
37
- expect(Appsignal::Transaction).to receive(:create).and_return(transaction)
38
- end
32
+ expect do
33
+ keep_transactions { job.perform }
34
+ end.to change { created_transactions.length }.by(1)
39
35
 
40
- it "wraps it in a transaction with the correct params" do
41
- expect(Appsignal).to receive(:monitor_transaction).with(
42
- "perform_job.resque",
43
- :class => "TestJob",
44
- :method => "perform"
36
+ expect(last_transaction).to be_completed
37
+ expect(last_transaction.to_h).to include(
38
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
39
+ "action" => "TestJob#perform",
40
+ "error" => nil,
41
+ "events" => [
42
+ hash_including(
43
+ "name" => "perform_job.resque",
44
+ "title" => "",
45
+ "body" => "",
46
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
47
+ "count" => 1,
48
+ "duration" => kind_of(Float)
49
+ )
50
+ ]
45
51
  )
46
52
  end
47
-
48
- it "closes the transaction" do
49
- expect(transaction).to receive(:complete)
50
- end
51
-
52
- after { job.perform }
53
53
  end
54
54
 
55
55
  context "with exception" do
56
56
  let(:job) { ::Resque::Job.new("default", "class" => "BrokenTestJob") }
57
- let(:transaction) do
58
- Appsignal::Transaction.new(
59
- SecureRandom.uuid,
60
- Appsignal::Transaction::BACKGROUND_JOB,
61
- Appsignal::Transaction::GenericRequest.new({})
62
- )
63
- end
64
- before do
65
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
66
- expect(Appsignal::Transaction).to receive(:create)
67
- .with(
68
- kind_of(String),
69
- Appsignal::Transaction::BACKGROUND_JOB,
70
- kind_of(Appsignal::Transaction::GenericRequest)
71
- ).and_return(transaction)
57
+
58
+ def perform
59
+ keep_transactions do
60
+ expect do
61
+ job.perform
62
+ end.to raise_error(ExampleException, "my error message")
63
+ end
72
64
  end
73
65
 
74
66
  it "sets the exception on the transaction" do
75
- expect(transaction).to receive(:set_error).with(ExampleException)
76
- end
67
+ expect do
68
+ perform
69
+ end.to change { created_transactions.length }.by(1)
77
70
 
78
- after do
79
- expect { job.perform }.to raise_error(ExampleException)
71
+ expect(last_transaction).to be_completed
72
+ expect(last_transaction.to_h).to include(
73
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
74
+ "action" => "BrokenTestJob#perform",
75
+ "error" => {
76
+ "name" => "ExampleException",
77
+ "message" => "my error message",
78
+ "backtrace" => kind_of(String)
79
+ }
80
+ )
80
81
  end
81
82
  end
82
83
  end
@@ -0,0 +1,25 @@
1
+ describe Appsignal::Logger do
2
+ let(:log) { std_stream }
3
+ let(:logger) do
4
+ Appsignal::Logger.new(log).tap do |l|
5
+ l.formatter = logger_formatter
6
+ end
7
+ end
8
+
9
+ describe "#seen_keys" do
10
+ it "returns a Set" do
11
+ expect(logger.seen_keys).to be_a(Set)
12
+ end
13
+ end
14
+
15
+ describe "#warn_once_then_debug" do
16
+ it "only warns once, then uses debug" do
17
+ message = "This is a log line"
18
+ 3.times { logger.warn_once_then_debug(:key, message) }
19
+
20
+ logs = log_contents(log)
21
+ expect(logs.scan(/#{Regexp.escape(log_line(:WARN, message))}/).count).to eql(1)
22
+ expect(logs.scan(/#{Regexp.escape(log_line(:DEBUG, message))}/).count).to eql(2)
23
+ end
24
+ end
25
+ end
@@ -189,7 +189,7 @@ describe Appsignal::Minutely do
189
189
  expect(Appsignal::Minutely).to have_received(:initial_wait_time).once
190
190
  expect do
191
191
  # Fetch old thread
192
- thread = Appsignal::Minutely.class_variable_get(:@@thread)
192
+ thread = Appsignal::Minutely.instance_variable_get(:@thread)
193
193
  Appsignal::Minutely.start
194
194
  thread && thread.join # Wait for old thread to exit
195
195
  end.to_not(change { alive_thread_counter.call })
@@ -203,7 +203,7 @@ describe Appsignal::Minutely do
203
203
 
204
204
  it "stops the minutely thread" do
205
205
  Appsignal::Minutely.start
206
- thread = Appsignal::Minutely.class_variable_get(:@@thread)
206
+ thread = Appsignal::Minutely.instance_variable_get(:@thread)
207
207
  expect(%w[sleep run]).to include(thread.status)
208
208
  Appsignal::Minutely.stop
209
209
  thread.join
@@ -213,7 +213,7 @@ describe Appsignal::Minutely do
213
213
  it "clears the probe instances array" do
214
214
  Appsignal::Minutely.probes.register :my_probe, lambda {}
215
215
  Appsignal::Minutely.start
216
- thread = Appsignal::Minutely.class_variable_get(:@@thread)
216
+ thread = Appsignal::Minutely.instance_variable_get(:@thread)
217
217
  wait_for("probes initialized") do
218
218
  !Appsignal::Minutely.send(:probe_instances).empty?
219
219
  end
@@ -285,19 +285,19 @@ describe Appsignal::Minutely do
285
285
  end
286
286
 
287
287
  describe "#<<" do
288
- let(:out_stream) { std_stream }
289
- let(:output) { out_stream.read }
288
+ let(:err_stream) { std_stream }
289
+ let(:stderr) { err_stream.read }
290
290
  let(:log_stream) { std_stream }
291
291
  let(:log) { log_contents(log_stream) }
292
292
  before { Appsignal.logger = test_logger(log_stream) }
293
293
 
294
294
  it "adds the probe, but prints a deprecation warning" do
295
295
  probe = lambda {}
296
- capture_stdout(out_stream) { collection << probe }
296
+ capture_std_streams(std_stream, err_stream) { collection << probe }
297
297
  deprecation_message = "Deprecated " \
298
298
  "`Appsignal::Minute.probes <<` call. Please use " \
299
299
  "`Appsignal::Minutely.probes.register` instead."
300
- expect(output).to include "appsignal WARNING: #{deprecation_message}"
300
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
301
301
  expect(log).to contains_log :warn, deprecation_message
302
302
  expect(collection[probe.object_id]).to eql(probe)
303
303
  end
@@ -3,19 +3,37 @@ describe Appsignal::Rack::JSExceptionCatcher do
3
3
  let(:options) { nil }
4
4
  let(:config_options) { { :enable_frontend_error_catching => true } }
5
5
  let(:config) { project_fixture_config("production", config_options) }
6
+ let(:deprecation_message) do
7
+ "The Appsignal::Rack::JSExceptionCatcher is deprecated. " \
8
+ "Please use the official AppSignal JavaScript integration instead. " \
9
+ "https://docs.appsignal.com/front-end/"
10
+ end
6
11
  before { Appsignal.config = config }
7
12
 
8
13
  describe "#initialize" do
9
- it "logs to the logger" do
10
- expect(Appsignal.logger).to receive(:debug)
11
- .with("Initializing Appsignal::Rack::JSExceptionCatcher")
14
+ let(:out_stream) { std_stream }
15
+ let(:err_stream) { std_stream }
16
+ let(:output) { out_stream.read }
17
+ let(:stderr) { err_stream.read }
12
18
 
13
- Appsignal::Rack::JSExceptionCatcher.new(app, options)
19
+ it "logs to the logger" do
20
+ log = capture_logs do
21
+ capture_std_streams(out_stream, err_stream) do
22
+ Appsignal::Rack::JSExceptionCatcher.new(app, options)
23
+ end
24
+ end
25
+ expect(log).to contains_log(:warn, deprecation_message)
26
+ expect(log).to contains_log(:debug, "Initializing Appsignal::Rack::JSExceptionCatcher")
27
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
28
+ expect(stderr).to_not include("appsignal:")
29
+ expect(output).to be_empty
14
30
  end
15
31
  end
16
32
 
17
33
  describe "#call" do
18
- let(:catcher) { Appsignal::Rack::JSExceptionCatcher.new(app, options) }
34
+ let(:catcher) do
35
+ silence { Appsignal::Rack::JSExceptionCatcher.new(app, options) }
36
+ end
19
37
  after { catcher.call(env) }
20
38
 
21
39
  context "when path is not frontend_error_catching_path" do
@@ -93,4 +93,40 @@ describe Appsignal::System do
93
93
  end
94
94
  end
95
95
  end
96
+
97
+ describe ".ruby_2_or_up?" do
98
+ around do |example|
99
+ original_ruby_version = RUBY_VERSION
100
+ Object.send(:remove_const, "RUBY_VERSION")
101
+ Object.const_set("RUBY_VERSION", ruby_version)
102
+ example.run
103
+ Object.send(:remove_const, "RUBY_VERSION")
104
+ Object.const_set("RUBY_VERSION", original_ruby_version)
105
+ end
106
+ subject { described_class.ruby_2_or_up? }
107
+
108
+ context "when on Ruby 1.9" do
109
+ let(:ruby_version) { "1.9.3-p533" }
110
+
111
+ it "returns false" do
112
+ is_expected.to be(false)
113
+ end
114
+ end
115
+
116
+ context "when on Ruby 2.0" do
117
+ let(:ruby_version) { "2.0.0" }
118
+
119
+ it "returns true" do
120
+ is_expected.to be(true)
121
+ end
122
+ end
123
+
124
+ context "when on Ruby 2.x" do
125
+ let(:ruby_version) { "2.1.0" }
126
+
127
+ it "returns true" do
128
+ is_expected.to be(true)
129
+ end
130
+ end
131
+ end
96
132
  end
@@ -64,7 +64,7 @@ describe Appsignal::Transaction do
64
64
 
65
65
  it "logs a debug message" do
66
66
  create_transaction("2")
67
- expect(log_contents(log)).to contains_log :debug,
67
+ expect(log_contents(log)).to contains_log :warn,
68
68
  "Trying to start new transaction with id '2', but a " \
69
69
  "transaction with id '#{transaction_id}' is already " \
70
70
  "running. Using transaction '#{transaction_id}'."
@@ -154,13 +154,8 @@ describe Appsignal::Transaction do
154
154
  describe "#complete" do
155
155
  context "when transaction is being sampled" do
156
156
  it "samples data" do
157
- expect(transaction.ext).to receive(:finish).and_return(true)
158
- # Stub call to extension, because that would remove the transaction
159
- # from the extension.
160
- expect(transaction.ext).to receive(:complete)
161
-
162
157
  transaction.set_tags(:foo => "bar")
163
- transaction.complete
158
+ keep_transactions { transaction.complete }
164
159
  expect(transaction.to_h["sample_data"]).to include(
165
160
  "tags" => { "foo" => "bar" }
166
161
  )
@@ -169,11 +164,8 @@ describe Appsignal::Transaction do
169
164
 
170
165
  context "when transaction is not being sampled" do
171
166
  it "does not sample data" do
172
- expect(transaction).to_not receive(:sample_data)
173
- expect(transaction.ext).to receive(:finish).and_return(false)
174
- expect(transaction.ext).to receive(:complete).and_call_original
175
-
176
- transaction.complete
167
+ keep_transactions(:sample => false) { transaction.complete }
168
+ expect(transaction.to_h["sample_data"]).to be_empty
177
169
  end
178
170
  end
179
171
 
@@ -449,30 +441,36 @@ describe Appsignal::Transaction do
449
441
 
450
442
  describe "#set_http_or_background_action" do
451
443
  context "for a hash with controller and action" do
452
- let(:from) { { :controller => "HomeController", :action => "show" } }
453
-
454
- it "should set the action" do
455
- expect(transaction).to receive(:set_action).with("HomeController#show")
444
+ it "sets the action" do
445
+ transaction.set_http_or_background_action(
446
+ :controller => "HomeController",
447
+ :action => "show"
448
+ )
449
+ expect(transaction.to_h["action"]).to eql("HomeController#show")
456
450
  end
457
451
  end
458
452
 
459
453
  context "for a hash with just action" do
460
- let(:from) { { :action => "show" } }
461
-
462
- it "should set the action" do
463
- expect(transaction).to receive(:set_action).with("show")
454
+ it "sets the action" do
455
+ transaction.set_http_or_background_action(:action => "show")
456
+ expect(transaction.to_h["action"]).to eql("show")
464
457
  end
465
458
  end
466
459
 
467
460
  context "for a hash with class and method" do
468
- let(:from) { { :class => "Worker", :method => "perform" } }
469
-
470
- it "should set the action" do
471
- expect(transaction).to receive(:set_action).with("Worker#perform")
461
+ it "sets the action" do
462
+ transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
463
+ expect(transaction.to_h["action"]).to eql("Worker#perform")
472
464
  end
473
465
  end
474
466
 
475
- after { transaction.set_http_or_background_action(from) }
467
+ context "when action is already set" do
468
+ it "does not overwrite the set action" do
469
+ transaction.set_action("MyCustomAction#perform")
470
+ transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
471
+ expect(transaction.to_h["action"]).to eql("MyCustomAction#perform")
472
+ end
473
+ end
476
474
  end
477
475
 
478
476
  describe "set_queue_start" do
@@ -576,7 +574,7 @@ describe Appsignal::Transaction do
576
574
  end
577
575
 
578
576
  context "when the data cannot be converted to JSON" do
579
- it "does not update the sample data on the transaction" do
577
+ it "does not update the sample data on the transaction", :not_ruby19 do
580
578
  klass = Class.new do
581
579
  def to_s
582
580
  raise "foo" # Cause a deliberate error
@@ -588,6 +586,19 @@ describe Appsignal::Transaction do
588
586
  expect(log_contents(log)).to contains_log :error,
589
587
  "Error generating data (RuntimeError: foo) for"
590
588
  end
589
+
590
+ it "does not update the sample data on the transaction", :only_ruby19 do
591
+ klass = Class.new do
592
+ def to_s
593
+ raise "foo" # Cause a deliberate error
594
+ end
595
+ end
596
+ transaction.set_sample_data("params", klass.new => 1)
597
+
598
+ expect(transaction.to_h["sample_data"]).to eq({})
599
+ expect(log_contents(log)).to contains_log :error,
600
+ "Error generating data (RuntimeError: foo). Can't inspect data."
601
+ end
591
602
  end
592
603
  end
593
604
 
@@ -143,7 +143,9 @@ describe Appsignal::Transmitter do
143
143
  context "with a proxy" do
144
144
  let(:config) { project_fixture_config("production", :http_proxy => "http://localhost:8080") }
145
145
 
146
- it { expect(subject).to be_instance_of(Net::HTTP) }
146
+ it "is of Net::HTTP class", :not_ruby19 do
147
+ expect(subject).to be_instance_of(Net::HTTP)
148
+ end
147
149
  it { expect(subject.proxy?).to be_truthy }
148
150
  it { expect(subject.proxy_address).to eq "localhost" }
149
151
  it { expect(subject.proxy_port).to eq 8080 }
@@ -30,7 +30,7 @@ describe Appsignal do
30
30
  context "with no config set beforehand" do
31
31
  it "should do nothing when config is not set and there is no valid config in the env" do
32
32
  expect(Appsignal.logger).to receive(:error).with(
33
- "Push api key not set after loading config"
33
+ "Push API key not set after loading config"
34
34
  ).once
35
35
  expect(Appsignal.logger).to receive(:error).with(
36
36
  "Not starting, no valid config for this environment"
@@ -701,13 +701,12 @@ describe Appsignal do
701
701
  context "when given a block" do
702
702
  it "yields the transaction and allows additional metadata to be set" do
703
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)
704
+ keep_transactions do
705
+ Appsignal.send_error(StandardError.new("my_error")) do |transaction|
706
+ captured_transaction = transaction
707
+ transaction.set_action("my_action")
708
+ transaction.set_namespace("my_namespace")
709
+ end
711
710
  end
712
711
  expect(captured_transaction.to_h).to include(
713
712
  "namespace" => "my_namespace",
@@ -950,7 +949,7 @@ describe Appsignal do
950
949
 
951
950
  it "outputs deprecated warning" do
952
951
  subject
953
- expect(stderr).to include("Appsignal.is_ignored_error? is deprecated with no replacement.")
952
+ expect(stderr).to include("Appsignal.is_ignored_error? is deprecated with no replacement")
954
953
  end
955
954
 
956
955
  context "when error is not in the ignored list" do
@@ -982,7 +981,7 @@ describe Appsignal do
982
981
 
983
982
  it "outputs deprecated warning" do
984
983
  subject
985
- expect(stderr).to include("Appsignal.is_ignored_action? is deprecated with no replacement.")
984
+ expect(stderr).to include("Appsignal.is_ignored_action? is deprecated with no replacement")
986
985
  end
987
986
 
988
987
  context "when action is not in the ingore list" do
@@ -1022,6 +1021,7 @@ describe Appsignal do
1022
1021
  Appsignal.start_logger
1023
1022
  Appsignal.logger.error("Log to file")
1024
1023
  end
1024
+ expect(Appsignal.logger).to be_a(Appsignal::Logger)
1025
1025
  end
1026
1026
 
1027
1027
  it "logs to file" do
@@ -1044,6 +1044,7 @@ describe Appsignal do
1044
1044
  initialize_config
1045
1045
  Appsignal.start_logger
1046
1046
  Appsignal.logger.error("Log to not writable log file")
1047
+ expect(Appsignal.logger).to be_a(Appsignal::Logger)
1047
1048
  end
1048
1049
  end
1049
1050
 
@@ -1074,6 +1075,7 @@ describe Appsignal do
1074
1075
  Appsignal.start_logger
1075
1076
  Appsignal.logger.error("Log to not writable log path")
1076
1077
  end
1078
+ expect(Appsignal.logger).to be_a(Appsignal::Logger)
1077
1079
  end
1078
1080
  after do
1079
1081
  FileUtils.chmod 0o755, Appsignal::Config.system_tmp_dir
@@ -1102,6 +1104,7 @@ describe Appsignal do
1102
1104
  Appsignal.start_logger
1103
1105
  Appsignal.logger.error("Log to stdout")
1104
1106
  end
1107
+ expect(Appsignal.logger).to be_a(Appsignal::Logger)
1105
1108
  end
1106
1109
  around { |example| recognize_as_heroku { example.run } }
1107
1110