appsignal 3.4.13 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +180 -14
  3. data/CHANGELOG.md +164 -0
  4. data/README.md +2 -0
  5. data/Rakefile +3 -1
  6. data/build_matrix.yml +7 -13
  7. data/ext/Rakefile +8 -1
  8. data/ext/agent.rb +27 -27
  9. data/ext/appsignal_extension.c +0 -24
  10. data/ext/base.rb +4 -1
  11. data/gemfiles/redis-4.gemfile +5 -0
  12. data/gemfiles/redis-5.gemfile +6 -0
  13. data/lib/appsignal/cli/diagnose/paths.rb +33 -10
  14. data/lib/appsignal/cli/diagnose.rb +6 -1
  15. data/lib/appsignal/config.rb +19 -5
  16. data/lib/appsignal/demo.rb +1 -1
  17. data/lib/appsignal/environment.rb +24 -13
  18. data/lib/appsignal/event_formatter.rb +1 -1
  19. data/lib/appsignal/extension/jruby.rb +4 -17
  20. data/lib/appsignal/extension.rb +1 -1
  21. data/lib/appsignal/helpers/instrumentation.rb +10 -10
  22. data/lib/appsignal/helpers/metrics.rb +15 -13
  23. data/lib/appsignal/hooks/active_job.rb +9 -1
  24. data/lib/appsignal/hooks/redis.rb +1 -0
  25. data/lib/appsignal/hooks/redis_client.rb +27 -0
  26. data/lib/appsignal/hooks.rb +3 -2
  27. data/lib/appsignal/integrations/hanami.rb +1 -1
  28. data/lib/appsignal/integrations/padrino.rb +1 -1
  29. data/lib/appsignal/integrations/railtie.rb +1 -1
  30. data/lib/appsignal/integrations/redis_client.rb +20 -0
  31. data/lib/appsignal/integrations/sidekiq.rb +2 -2
  32. data/lib/appsignal/integrations/sinatra.rb +1 -1
  33. data/lib/appsignal/logger.rb +2 -0
  34. data/lib/appsignal/minutely.rb +4 -4
  35. data/lib/appsignal/probes/gvl.rb +1 -1
  36. data/lib/appsignal/probes/helpers.rb +1 -1
  37. data/lib/appsignal/probes/mri.rb +1 -1
  38. data/lib/appsignal/probes/sidekiq.rb +10 -8
  39. data/lib/appsignal/rack/body_wrapper.rb +161 -0
  40. data/lib/appsignal/rack/generic_instrumentation.rb +18 -5
  41. data/lib/appsignal/rack/rails_instrumentation.rb +17 -5
  42. data/lib/appsignal/rack/sinatra_instrumentation.rb +17 -5
  43. data/lib/appsignal/rack/streaming_listener.rb +27 -36
  44. data/lib/appsignal/span.rb +2 -2
  45. data/lib/appsignal/transaction.rb +46 -10
  46. data/lib/appsignal/utils/deprecation_message.rb +2 -2
  47. data/lib/appsignal/version.rb +1 -1
  48. data/lib/appsignal.rb +38 -31
  49. data/resources/cacert.pem +321 -159
  50. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +11 -0
  51. data/spec/lib/appsignal/cli/diagnose_spec.rb +38 -12
  52. data/spec/lib/appsignal/config_spec.rb +3 -2
  53. data/spec/lib/appsignal/hooks/activejob_spec.rb +26 -1
  54. data/spec/lib/appsignal/hooks/redis_client_spec.rb +222 -0
  55. data/spec/lib/appsignal/hooks/redis_spec.rb +98 -76
  56. data/spec/lib/appsignal/hooks_spec.rb +4 -4
  57. data/spec/lib/appsignal/integrations/railtie_spec.rb +2 -2
  58. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +3 -3
  59. data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
  60. data/spec/lib/appsignal/minutely_spec.rb +2 -2
  61. data/spec/lib/appsignal/probes/sidekiq_spec.rb +29 -6
  62. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +220 -0
  63. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +3 -2
  64. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +5 -3
  65. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -1
  66. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +9 -53
  67. data/spec/lib/appsignal/transaction_spec.rb +95 -2
  68. data/spec/lib/appsignal_spec.rb +62 -60
  69. data/spec/spec_helper.rb +1 -1
  70. data/spec/support/fixtures/projects/valid/config/appsignal.yml +3 -3
  71. data/spec/support/helpers/config_helpers.rb +6 -2
  72. data/spec/support/helpers/dependency_helper.rb +9 -1
  73. data/spec/support/helpers/log_helpers.rb +2 -2
  74. metadata +9 -2
@@ -152,6 +152,7 @@ describe Appsignal::Config do
152
152
  it "merges with the default config" do
153
153
  expect(config.config_hash).to eq(
154
154
  :active => true,
155
+ :activejob_report_errors => "all",
155
156
  :ca_file_path => File.join(resources_dir, "cacert.pem"),
156
157
  :debug => false,
157
158
  :dns_servers => [],
@@ -265,7 +266,7 @@ describe Appsignal::Config do
265
266
 
266
267
  context "with an overriden config file" do
267
268
  let(:config) do
268
- project_fixture_config("production", {}, Appsignal.logger,
269
+ project_fixture_config("production", {}, Appsignal.internal_logger,
269
270
  File.join(project_fixture_path, "config", "appsignal.yml"))
270
271
  end
271
272
 
@@ -276,7 +277,7 @@ describe Appsignal::Config do
276
277
 
277
278
  context "with an invalid overriden config file" do
278
279
  let(:config) do
279
- project_fixture_config("production", {}, Appsignal.logger,
280
+ project_fixture_config("production", {}, Appsignal.internal_logger,
280
281
  File.join(project_fixture_path, "config", "missing.yml"))
281
282
  end
282
283
 
@@ -76,7 +76,7 @@ if DependencyHelper.active_job_present?
76
76
  ActiveJob::Base.queue_adapter = :inline
77
77
 
78
78
  start_agent
79
- Appsignal.logger = test_logger(log)
79
+ Appsignal.internal_logger = test_logger(log)
80
80
  class ActiveJobTestJob < ActiveJob::Base
81
81
  def perform(*_args)
82
82
  end
@@ -222,6 +222,31 @@ if DependencyHelper.active_job_present?
222
222
  expect(events).to eq(expected_perform_events)
223
223
  end
224
224
 
225
+ context "with activejob_report_errors set to none" do
226
+ it "does not report the error" do
227
+ Appsignal.config = project_fixture_config("production")
228
+ Appsignal.config[:activejob_report_errors] = "none"
229
+
230
+ # Other calls we're testing in another test
231
+ allow(Appsignal).to receive(:increment_counter)
232
+ tags = { :queue => queue }
233
+ expect(Appsignal).to receive(:increment_counter)
234
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :failed))
235
+ expect(Appsignal).to receive(:increment_counter)
236
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
237
+
238
+ expect do
239
+ perform_job(ActiveJobErrorTestJob)
240
+ end.to raise_error(RuntimeError, "uh oh")
241
+
242
+ transaction = last_transaction
243
+ transaction_hash = transaction.to_h
244
+ expect(transaction_hash).to include(
245
+ "error" => nil
246
+ )
247
+ end
248
+ end
249
+
225
250
  if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
226
251
  context "with priority" do
227
252
  before do
@@ -0,0 +1,222 @@
1
+ describe Appsignal::Hooks::RedisClientHook do
2
+ before do
3
+ Appsignal.config = project_fixture_config
4
+ end
5
+
6
+ if DependencyHelper.redis_client_present?
7
+ context "with redis_client" do
8
+ context "with instrumentation enabled" do
9
+ describe "#dependencies_present?" do
10
+ subject { described_class.new.dependencies_present? }
11
+
12
+ it { is_expected.to be_truthy }
13
+ end
14
+
15
+ context "with rest-client gem" do
16
+ describe "integration" do
17
+ before do
18
+ Appsignal.config.config_hash[:instrument_redis] = true
19
+ end
20
+
21
+ context "install" do
22
+ before do
23
+ Appsignal::Hooks.load_hooks
24
+ end
25
+
26
+ it "includes the integration for the ruby connection" do
27
+ # Test if the last included module (prepended module) was our
28
+ # integration. That's not certain with the assertions below
29
+ # because we have to overwrite the `process` method for the test.
30
+ expect(RedisClient::RubyConnection.included_modules.first)
31
+ .to eql(Appsignal::Integrations::RedisClientIntegration)
32
+ end
33
+ end
34
+
35
+ context "requirements" do
36
+ it "driver should have the write method" do
37
+ # Since we stub the driver class below, to make sure that we don't
38
+ # create a real connection, the test won't fail if the method definition
39
+ # is changed.
40
+ method = RedisClient::RubyConnection.instance_method(:write)
41
+ expect(method.arity).to eql(1)
42
+ end
43
+ end
44
+
45
+ context "instrumentation" do
46
+ before do
47
+ start_agent
48
+ # Stub RedisClient::RubyConnection class so that it doesn't perform an actual
49
+ # Redis query. This class will be included (prepended) with the
50
+ # AppSignal Redis integration.
51
+ stub_const("RedisClient::RubyConnection", Class.new do
52
+ def initialize(config)
53
+ @config = config
54
+ end
55
+
56
+ def write(_commands)
57
+ "stub_write"
58
+ end
59
+ end)
60
+ # Load the integration again for the stubbed RedisClient::RubyConnection class.
61
+ # Call it directly because {Appsignal::Hooks.load_hooks} keeps
62
+ # track if it was installed already or not.
63
+ Appsignal::Hooks::RedisClientHook.new.install
64
+ end
65
+ let!(:transaction) do
66
+ Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
67
+ end
68
+ let!(:client_config) { RedisClient::Config.new(:id => "stub_id") }
69
+ around { |example| keep_transactions { example.run } }
70
+
71
+ it "instrument a redis call" do
72
+ connection = RedisClient::RubyConnection.new client_config
73
+ expect(connection.write([:get, "key"])).to eql("stub_write")
74
+
75
+ transaction_hash = transaction.to_h
76
+ expect(transaction_hash["events"]).to include(
77
+ hash_including(
78
+ "name" => "query.redis",
79
+ "body" => "get ?",
80
+ "title" => "stub_id"
81
+ )
82
+ )
83
+ end
84
+
85
+ it "instrument a redis script call" do
86
+ connection = ::RedisClient::RubyConnection.new client_config
87
+ script = "return redis.call('set',KEYS[1],ARGV[1])"
88
+ keys = ["foo"]
89
+ argv = ["bar"]
90
+ expect(connection.write([:eval, script, keys.size, keys,
91
+ argv])).to eql("stub_write")
92
+
93
+ transaction_hash = transaction.to_h
94
+ expect(transaction_hash["events"]).to include(
95
+ hash_including(
96
+ "name" => "query.redis",
97
+ "body" => "#{script} ? ?",
98
+ "title" => "stub_id"
99
+ )
100
+ )
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ if DependencyHelper.hiredis_client_present?
107
+ context "with hiredis driver" do
108
+ describe "integration" do
109
+ before do
110
+ Appsignal.config.config_hash[:instrument_redis] = true
111
+ end
112
+
113
+ context "install" do
114
+ before do
115
+ Appsignal::Hooks.load_hooks
116
+ end
117
+
118
+ it "includes the integration in the HiredisConnection class" do
119
+ # Test if the last included module (prepended module) was our
120
+ # integration. That's not certain with the assertions below
121
+ # because we have to overwrite the `process` method for the test.
122
+ expect(RedisClient::HiredisConnection.included_modules.first)
123
+ .to eql(Appsignal::Integrations::RedisClientIntegration)
124
+ end
125
+ end
126
+
127
+ context "requirements" do
128
+ it "driver should have the write method" do
129
+ # Since we stub the driver class below, to make sure that we don't
130
+ # create a real connection, the test won't fail if the method definition
131
+ # is changed.
132
+ method = RedisClient::HiredisConnection.instance_method(:write)
133
+ expect(method.arity).to eql(1)
134
+ end
135
+ end
136
+
137
+ context "instrumentation" do
138
+ before do
139
+ start_agent
140
+ # Stub RedisClient::HiredisConnection class so that it doesn't perform an actual
141
+ # Redis query. This class will be included (prepended) with the
142
+ # AppSignal Redis integration.
143
+ stub_const("RedisClient::HiredisConnection", Class.new do
144
+ def initialize(config)
145
+ @config = config
146
+ end
147
+
148
+ def write(_commands)
149
+ "stub_write"
150
+ end
151
+ end)
152
+ # Load the integration again for the stubbed RedisClient::HiredisConnection class.
153
+ # Call it directly because {Appsignal::Hooks.load_hooks} keeps
154
+ # track if it was installed already or not.
155
+ Appsignal::Hooks::RedisClientHook.new.install
156
+ end
157
+ let!(:transaction) do
158
+ Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST,
159
+ "test")
160
+ end
161
+ let!(:client_config) { RedisClient::Config.new(:id => "stub_id") }
162
+ around { |example| keep_transactions { example.run } }
163
+
164
+ it "instrument a redis call" do
165
+ connection = RedisClient::HiredisConnection.new client_config
166
+ expect(connection.write([:get, "key"])).to eql("stub_write")
167
+
168
+ transaction_hash = transaction.to_h
169
+ expect(transaction_hash["events"]).to include(
170
+ hash_including(
171
+ "name" => "query.redis",
172
+ "body" => "get ?",
173
+ "title" => "stub_id"
174
+ )
175
+ )
176
+ end
177
+
178
+ it "instrument a redis script call" do
179
+ connection = ::RedisClient::HiredisConnection.new client_config
180
+ script = "return redis.call('set',KEYS[1],ARGV[1])"
181
+ keys = ["foo"]
182
+ argv = ["bar"]
183
+ expect(connection.write([:eval, script, keys.size, keys,
184
+ argv])).to eql("stub_write")
185
+
186
+ transaction_hash = transaction.to_h
187
+ expect(transaction_hash["events"]).to include(
188
+ hash_including(
189
+ "name" => "query.redis",
190
+ "body" => "#{script} ? ?",
191
+ "title" => "stub_id"
192
+ )
193
+ )
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ context "with instrumentation disabled" do
202
+ before do
203
+ Appsignal.config.config_hash[:instrument_redis] = false
204
+ end
205
+
206
+ describe "#dependencies_present?" do
207
+ subject { described_class.new.dependencies_present? }
208
+
209
+ it { is_expected.to be_falsy }
210
+ end
211
+ end
212
+ end
213
+ else
214
+ context "without redis" do
215
+ describe "#dependencies_present?" do
216
+ subject { described_class.new.dependencies_present? }
217
+
218
+ it { is_expected.to be_falsy }
219
+ end
220
+ end
221
+ end
222
+ end
@@ -5,100 +5,122 @@ describe Appsignal::Hooks::RedisHook do
5
5
 
6
6
  if DependencyHelper.redis_present?
7
7
  context "with redis" do
8
- context "with instrumentation enabled" do
9
- describe "#dependencies_present?" do
10
- subject { described_class.new.dependencies_present? }
8
+ if DependencyHelper.redis_client_present?
9
+ context "with redis-client" do
10
+ context "with instrumentation enabled" do
11
+ describe "#dependencies_present?" do
12
+ subject { described_class.new.dependencies_present? }
11
13
 
12
- it { is_expected.to be_truthy }
14
+ it { is_expected.to be_falsey }
15
+ end
16
+ end
13
17
  end
18
+ else
19
+ context "with instrumentation enabled" do
20
+ describe "#dependencies_present?" do
21
+ subject { described_class.new.dependencies_present? }
14
22
 
15
- describe "integration" do
16
- before do
17
- Appsignal.config.config_hash[:instrument_redis] = true
23
+ it { is_expected.to be_truthy }
18
24
  end
19
25
 
20
- context "install" do
26
+ describe "integration" do
21
27
  before do
22
- Appsignal::Hooks.load_hooks
28
+ Appsignal.config.config_hash[:instrument_redis] = true
23
29
  end
24
30
 
25
- it "does something" do
26
- # Test if the last included module (prepended module) was our
27
- # integration. That's not certain with the assertions below
28
- # because we have to overwrite the `process` method for the test.
29
- expect(Redis::Client.included_modules.first)
30
- .to eql(Appsignal::Integrations::RedisIntegration)
31
- end
32
- end
31
+ context "install" do
32
+ before do
33
+ Appsignal::Hooks.load_hooks
34
+ end
33
35
 
34
- context "instrumentation" do
35
- before do
36
- start_agent
37
- # Stub Redis::Client class so that it doesn't perform an actual
38
- # Redis query. This class will be included (prepended) with the
39
- # AppSignal Redis integration.
40
- stub_const("Redis::Client", Class.new do
41
- def id
42
- "stub_id"
43
- end
44
-
45
- def write(_commands)
46
- "stub_write"
47
- end
48
- end)
49
- # Load the integration again for the stubbed Redis::Client class.
50
- # Call it directly because {Appsignal::Hooks.load_hooks} keeps
51
- # track if it was installed already or not.
52
- Appsignal::Hooks::RedisHook.new.install
53
- end
54
- let!(:transaction) do
55
- Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
36
+ it "prepends instrumentation module" do
37
+ # Test if the last included module (prepended module) was our
38
+ # integration. That's not certain with the assertions below
39
+ # because we have to overwrite the `process` method for the test.
40
+ expect(Redis::Client.included_modules.first)
41
+ .to eql(Appsignal::Integrations::RedisIntegration)
42
+ end
56
43
  end
57
- around { |example| keep_transactions { example.run } }
58
-
59
- it "instrument a redis call" do
60
- client = Redis::Client.new
61
- expect(client.write([:get, "key"])).to eql("stub_write")
62
-
63
- transaction_hash = transaction.to_h
64
- expect(transaction_hash["events"]).to include(
65
- hash_including(
66
- "name" => "query.redis",
67
- "body" => "get ?",
68
- "title" => "stub_id"
69
- )
70
- )
44
+
45
+ context "requirements" do
46
+ it "driver should have the write method" do
47
+ # Since we stub the client class below, to make sure that we don't
48
+ # create a real connection, the test won't fail if the method definition
49
+ # is changed.
50
+ method = Redis::Client.instance_method(:call)
51
+ expect(method.arity).to eql(1)
52
+ end
71
53
  end
72
54
 
73
- it "instrument a redis script call" do
74
- client = Redis::Client.new
75
- script = "return redis.call('set',KEYS[1],ARGV[1])"
76
- keys = ["foo"]
77
- argv = ["bar"]
78
- expect(client.write([:eval, script, keys.size, keys, argv])).to eql("stub_write")
79
-
80
- transaction_hash = transaction.to_h
81
- expect(transaction_hash["events"]).to include(
82
- hash_including(
83
- "name" => "query.redis",
84
- "body" => "#{script} ? ?",
85
- "title" => "stub_id"
86
- )
87
- )
55
+ context "instrumentation" do
56
+ before do
57
+ start_agent
58
+ # Stub Redis::Client class so that it doesn't perform an actual
59
+ # Redis query. This class will be included (prepended) with the
60
+ # AppSignal Redis integration.
61
+ stub_const("Redis::Client", Class.new do
62
+ def id
63
+ "stub_id"
64
+ end
65
+
66
+ def write(_commands)
67
+ "stub_write"
68
+ end
69
+ end)
70
+ # Load the integration again for the stubbed Redis::Client class.
71
+ # Call it directly because {Appsignal::Hooks.load_hooks} keeps
72
+ # track if it was installed already or not.
73
+ Appsignal::Hooks::RedisHook.new.install
74
+ end
75
+ let!(:transaction) do
76
+ Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
77
+ end
78
+ around { |example| keep_transactions { example.run } }
79
+
80
+ it "instrument a redis call" do
81
+ client = Redis::Client.new
82
+ expect(client.write([:get, "key"])).to eql("stub_write")
83
+
84
+ transaction_hash = transaction.to_h
85
+ expect(transaction_hash["events"]).to include(
86
+ hash_including(
87
+ "name" => "query.redis",
88
+ "body" => "get ?",
89
+ "title" => "stub_id"
90
+ )
91
+ )
92
+ end
93
+
94
+ it "instrument a redis script call" do
95
+ client = Redis::Client.new
96
+ script = "return redis.call('set',KEYS[1],ARGV[1])"
97
+ keys = ["foo"]
98
+ argv = ["bar"]
99
+ expect(client.write([:eval, script, keys.size, keys, argv])).to eql("stub_write")
100
+
101
+ transaction_hash = transaction.to_h
102
+ expect(transaction_hash["events"]).to include(
103
+ hash_including(
104
+ "name" => "query.redis",
105
+ "body" => "#{script} ? ?",
106
+ "title" => "stub_id"
107
+ )
108
+ )
109
+ end
88
110
  end
89
111
  end
90
112
  end
91
- end
92
113
 
93
- context "with instrumentation disabled" do
94
- before do
95
- Appsignal.config.config_hash[:instrument_redis] = false
96
- end
114
+ context "with instrumentation disabled" do
115
+ before do
116
+ Appsignal.config.config_hash[:instrument_redis] = false
117
+ end
97
118
 
98
- describe "#dependencies_present?" do
99
- subject { described_class.new.dependencies_present? }
119
+ describe "#dependencies_present?" do
120
+ subject { described_class.new.dependencies_present? }
100
121
 
101
- it { is_expected.to be_falsy }
122
+ it { is_expected.to be_falsy }
123
+ end
102
124
  end
103
125
  end
104
126
  end
@@ -67,12 +67,12 @@ describe Appsignal::Hooks do
67
67
  expect(Appsignal::Hooks.hooks[:mock_error_hook]).to be_instance_of(MockErrorHook)
68
68
  expect(Appsignal::Hooks.hooks[:mock_error_hook].installed?).to be_falsy
69
69
 
70
- expect(Appsignal.logger).to receive(:error)
70
+ expect(Appsignal.internal_logger).to receive(:error)
71
71
  .with("Error while installing mock_error_hook hook: error").once
72
- expect(Appsignal.logger).to receive(:debug).ordered do |message|
72
+ expect(Appsignal.internal_logger).to receive(:debug).ordered do |message|
73
73
  expect(message).to eq("Installing mock_error_hook hook")
74
74
  end
75
- expect(Appsignal.logger).to receive(:debug).ordered do |message|
75
+ expect(Appsignal.internal_logger).to receive(:debug).ordered do |message|
76
76
  # Start of the error backtrace as debug log
77
77
  expect(message).to start_with(File.expand_path("../../..", __dir__))
78
78
  end
@@ -89,7 +89,7 @@ describe Appsignal::Hooks do
89
89
  let(:log_stream) { std_stream }
90
90
  let(:log) { log_contents(log_stream) }
91
91
  before do
92
- Appsignal.logger = test_logger(log_stream)
92
+ Appsignal.internal_logger = test_logger(log_stream)
93
93
  end
94
94
 
95
95
  def call_constant(&block)
@@ -18,9 +18,9 @@ if DependencyHelper.rails_present?
18
18
  describe "#initialize_appsignal" do
19
19
  let(:app) { MyApp::Application.new }
20
20
 
21
- describe ".logger" do
21
+ describe ".internal_logger" do
22
22
  before { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
23
- subject { Appsignal.logger }
23
+ subject { Appsignal.internal_logger }
24
24
 
25
25
  it { is_expected.to be_a Logger }
26
26
  end
@@ -4,7 +4,7 @@ describe Appsignal::Integrations::SidekiqErrorHandler do
4
4
  let(:log) { StringIO.new }
5
5
  before do
6
6
  start_agent
7
- Appsignal.logger = test_logger(log)
7
+ Appsignal.internal_logger = test_logger(log)
8
8
  end
9
9
  around { |example| keep_transactions { example.run } }
10
10
 
@@ -86,7 +86,7 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
86
86
  let(:log) { StringIO.new }
87
87
  before do
88
88
  start_agent
89
- Appsignal.logger = test_logger(log)
89
+ Appsignal.internal_logger = test_logger(log)
90
90
  end
91
91
  around { |example| keep_transactions { example.run } }
92
92
  after :with_yaml_parse_error => false do
@@ -423,7 +423,7 @@ if DependencyHelper.active_job_present?
423
423
  end
424
424
  around do |example|
425
425
  start_agent
426
- Appsignal.logger = test_logger(log)
426
+ Appsignal.internal_logger = test_logger(log)
427
427
  ActiveJob::Base.queue_adapter = :sidekiq
428
428
 
429
429
  class ActiveJobSidekiqTestJob < ActiveJob::Base
@@ -16,8 +16,8 @@ if DependencyHelper.sinatra_present?
16
16
  before { allow(Appsignal).to receive(:active?).and_return(true) }
17
17
  after { uninstall_sinatra_integration }
18
18
 
19
- context "Appsignal.logger" do
20
- subject { Appsignal.logger }
19
+ context "Appsignal.internal_logger" do
20
+ subject { Appsignal.internal_logger }
21
21
 
22
22
  it "sets a logger" do
23
23
  install_sinatra_integration
@@ -42,7 +42,7 @@ describe Appsignal::Minutely do
42
42
  let(:log_stream) { StringIO.new }
43
43
  let(:log) { log_contents(log_stream) }
44
44
  before do
45
- Appsignal.logger = test_logger(log_stream)
45
+ Appsignal.internal_logger = test_logger(log_stream)
46
46
  # Speed up test time
47
47
  allow(Appsignal::Minutely).to receive(:initial_wait_time).and_return(0.001)
48
48
  allow(Appsignal::Minutely).to receive(:wait_time).and_return(0.001)
@@ -287,7 +287,7 @@ describe Appsignal::Minutely do
287
287
  describe "#register" do
288
288
  let(:log_stream) { std_stream }
289
289
  let(:log) { log_contents(log_stream) }
290
- before { Appsignal.logger = test_logger(log_stream) }
290
+ before { Appsignal.internal_logger = test_logger(log_stream) }
291
291
 
292
292
  it "adds the by key probe" do
293
293
  probe = lambda {}
@@ -73,6 +73,20 @@ describe Appsignal::Probes::SidekiqProbe do
73
73
  module Sidekiq7Mock
74
74
  VERSION = "7.0.0".freeze
75
75
 
76
+ def self.redis_info_data=(info)
77
+ @redis_info_data = info
78
+ end
79
+
80
+ def self.redis_info_data
81
+ return @redis_info_data if defined?(@redis_info_data)
82
+
83
+ {
84
+ "connected_clients" => 2,
85
+ "used_memory" => 1024,
86
+ "used_memory_rss" => 512
87
+ }
88
+ end
89
+
76
90
  def self.redis
77
91
  yield Client.new
78
92
  end
@@ -83,11 +97,7 @@ describe Appsignal::Probes::SidekiqProbe do
83
97
  end
84
98
 
85
99
  def info
86
- {
87
- "connected_clients" => 2,
88
- "used_memory" => 1024,
89
- "used_memory_rss" => 512
90
- }
100
+ Sidekiq7Mock.redis_info_data
91
101
  end
92
102
  end
93
103
 
@@ -227,6 +237,19 @@ describe Appsignal::Probes::SidekiqProbe do
227
237
  probe.call
228
238
  probe.call
229
239
  end
240
+
241
+ context "when redis info doesn't contain requested keys" do
242
+ before { Sidekiq7Mock.redis_info_data = {} }
243
+
244
+ it "doesn't create metrics for nil values" do
245
+ expect_gauge("connection_count").never
246
+ expect_gauge("memory_usage").never
247
+ expect_gauge("memory_usage_rss").never
248
+ # Call probe twice so we can calculate the delta for some gauge values
249
+ probe.call
250
+ probe.call
251
+ end
252
+ end
230
253
  end
231
254
 
232
255
  context "with Sidekiq 6" do
@@ -301,7 +324,7 @@ describe Appsignal::Probes::SidekiqProbe do
301
324
  end
302
325
  end
303
326
 
304
- def expect_gauge(key, value, tags = {})
327
+ def expect_gauge(key, value = anything, tags = {})
305
328
  expect(Appsignal).to receive(:set_gauge)
306
329
  .with("sidekiq_#{key}", value, expected_default_tags.merge(tags))
307
330
  .and_call_original