appsignal 2.11.0.alpha.1 → 2.11.0.beta.4

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.semaphore/semaphore.yml +75 -61
  4. data/CHANGELOG.md +21 -1
  5. data/build_matrix.yml +13 -7
  6. data/ext/agent.yml +19 -19
  7. data/gemfiles/padrino.gemfile +2 -2
  8. data/gemfiles/rails-4.2.gemfile +9 -2
  9. data/gemfiles/rails-5.0.gemfile +1 -0
  10. data/gemfiles/rails-5.1.gemfile +1 -0
  11. data/gemfiles/rails-5.2.gemfile +1 -0
  12. data/gemfiles/rails-6.0.gemfile +1 -0
  13. data/gemfiles/resque-1.gemfile +7 -0
  14. data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
  15. data/lib/appsignal/hooks.rb +2 -0
  16. data/lib/appsignal/hooks/active_job.rb +114 -0
  17. data/lib/appsignal/hooks/puma.rb +2 -58
  18. data/lib/appsignal/hooks/resque.rb +60 -0
  19. data/lib/appsignal/hooks/sidekiq.rb +19 -192
  20. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
  21. data/lib/appsignal/integrations/que.rb +1 -1
  22. data/lib/appsignal/integrations/resque.rb +9 -12
  23. data/lib/appsignal/integrations/resque_active_job.rb +9 -32
  24. data/lib/appsignal/probes/puma.rb +61 -0
  25. data/lib/appsignal/probes/sidekiq.rb +102 -0
  26. data/lib/appsignal/rack/js_exception_catcher.rb +5 -2
  27. data/lib/appsignal/transaction.rb +10 -0
  28. data/lib/appsignal/utils/deprecation_message.rb +5 -1
  29. data/lib/appsignal/version.rb +1 -1
  30. data/lib/puma/plugin/appsignal.rb +2 -1
  31. data/spec/lib/appsignal/hooks/activejob_spec.rb +548 -0
  32. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +3 -14
  33. data/spec/lib/appsignal/hooks/puma_spec.rb +2 -181
  34. data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
  35. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +297 -549
  36. data/spec/lib/appsignal/integrations/padrino_spec.rb +1 -1
  37. data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
  38. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -179
  39. data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
  40. data/spec/lib/appsignal/probes/puma_spec.rb +180 -0
  41. data/spec/lib/appsignal/probes/sidekiq_spec.rb +204 -0
  42. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +9 -4
  43. data/spec/lib/appsignal/transaction_spec.rb +5 -7
  44. data/spec/lib/puma/appsignal_spec.rb +1 -1
  45. data/spec/support/helpers/action_mailer_helpers.rb +25 -0
  46. data/spec/support/helpers/dependency_helper.rb +9 -2
  47. data/spec/support/helpers/transaction_helpers.rb +6 -0
  48. data/spec/support/stubs/sidekiq/api.rb +2 -2
  49. metadata +18 -3
@@ -0,0 +1,204 @@
1
+ require "appsignal/probes/sidekiq"
2
+
3
+ describe Appsignal::Probes::SidekiqProbe do
4
+ describe "#call" do
5
+ let(:probe) { described_class.new }
6
+ let(:redis_hostname) { "localhost" }
7
+ let(:expected_default_tags) { { :hostname => "localhost" } }
8
+ before do
9
+ Appsignal.config = project_fixture_config
10
+ module SidekiqMock
11
+ def self.redis_info
12
+ {
13
+ "connected_clients" => 2,
14
+ "used_memory" => 1024,
15
+ "used_memory_rss" => 512
16
+ }
17
+ end
18
+
19
+ def self.redis
20
+ yield Client.new
21
+ end
22
+
23
+ class Client
24
+ def connection
25
+ { :host => "localhost" }
26
+ end
27
+ end
28
+
29
+ class Stats
30
+ class << self
31
+ attr_reader :calls
32
+
33
+ def count_call
34
+ @calls ||= -1
35
+ @calls += 1
36
+ end
37
+ end
38
+
39
+ def workers_size
40
+ # First method called, so count it towards a call
41
+ self.class.count_call
42
+ 24
43
+ end
44
+
45
+ def processes_size
46
+ 25
47
+ end
48
+
49
+ # Return two different values for two separate calls.
50
+ # This allows us to test the delta of the value send as a gauge.
51
+ def processed
52
+ [10, 15][self.class.calls]
53
+ end
54
+
55
+ # Return two different values for two separate calls.
56
+ # This allows us to test the delta of the value send as a gauge.
57
+ def failed
58
+ [10, 13][self.class.calls]
59
+ end
60
+
61
+ def retry_size
62
+ 12
63
+ end
64
+
65
+ # Return two different values for two separate calls.
66
+ # This allows us to test the delta of the value send as a gauge.
67
+ def dead_size
68
+ [10, 12][self.class.calls]
69
+ end
70
+
71
+ def scheduled_size
72
+ 14
73
+ end
74
+
75
+ def enqueued
76
+ 15
77
+ end
78
+ end
79
+
80
+ class Queue
81
+ Queue = Struct.new(:name, :size, :latency)
82
+
83
+ def self.all
84
+ [
85
+ Queue.new("default", 10, 12),
86
+ Queue.new("critical", 1, 2)
87
+ ]
88
+ end
89
+ end
90
+ end
91
+ stub_const("Sidekiq", SidekiqMock)
92
+ end
93
+ after { Object.send(:remove_const, :SidekiqMock) }
94
+
95
+ describe ".dependencies_present?" do
96
+ before do
97
+ stub_const("Redis::VERSION", version)
98
+ end
99
+
100
+ context "when Redis version is < 3.3.5" do
101
+ let(:version) { "3.3.4" }
102
+
103
+ it "does not start probe" do
104
+ expect(described_class.dependencies_present?).to be_falsy
105
+ end
106
+ end
107
+
108
+ context "when Redis version is >= 3.3.5" do
109
+ let(:version) { "3.3.5" }
110
+
111
+ it "does not start probe" do
112
+ expect(described_class.dependencies_present?).to be_truthy
113
+ end
114
+ end
115
+ end
116
+
117
+ it "loads Sidekiq::API" do
118
+ # Hide the Sidekiq constant if it was already loaded. It will be
119
+ # redefined by loading "sidekiq/api" in the probe.
120
+ hide_const "Sidekiq::Stats"
121
+
122
+ expect(defined?(Sidekiq::Stats)).to be_falsy
123
+ probe
124
+ expect(defined?(Sidekiq::Stats)).to be_truthy
125
+ end
126
+
127
+ it "logs config on initialize" do
128
+ log = capture_logs { probe }
129
+ expect(log).to contains_log(:debug, "Initializing Sidekiq probe\n")
130
+ end
131
+
132
+ it "logs used hostname on call once" do
133
+ log = capture_logs { probe.call }
134
+ expect(log).to contains_log(
135
+ :debug,
136
+ %(Sidekiq probe: Using Redis server hostname "localhost" as hostname)
137
+ )
138
+ log = capture_logs { probe.call }
139
+ # Match more logs with incompelete message
140
+ expect(log).to_not contains_log(:debug, %(Sidekiq probe: ))
141
+ end
142
+
143
+ it "collects custom metrics" do
144
+ expect_gauge("worker_count", 24).twice
145
+ expect_gauge("process_count", 25).twice
146
+ expect_gauge("connection_count", 2).twice
147
+ expect_gauge("memory_usage", 1024).twice
148
+ expect_gauge("memory_usage_rss", 512).twice
149
+ expect_gauge("job_count", 5, :status => :processed) # Gauge delta
150
+ expect_gauge("job_count", 3, :status => :failed) # Gauge delta
151
+ expect_gauge("job_count", 12, :status => :retry_queue).twice
152
+ expect_gauge("job_count", 2, :status => :died) # Gauge delta
153
+ expect_gauge("job_count", 14, :status => :scheduled).twice
154
+ expect_gauge("job_count", 15, :status => :enqueued).twice
155
+ expect_gauge("queue_length", 10, :queue => "default").twice
156
+ expect_gauge("queue_latency", 12_000, :queue => "default").twice
157
+ expect_gauge("queue_length", 1, :queue => "critical").twice
158
+ expect_gauge("queue_latency", 2_000, :queue => "critical").twice
159
+ # Call probe twice so we can calculate the delta for some gauge values
160
+ probe.call
161
+ probe.call
162
+ end
163
+
164
+ context "when `redis_info` is not defined" do
165
+ before do
166
+ allow(Sidekiq).to receive(:respond_to?).with(:redis_info).and_return(false)
167
+ end
168
+
169
+ it "does not collect redis metrics" do
170
+ expect_gauge("connection_count", 2).never
171
+ expect_gauge("memory_usage", 1024).never
172
+ expect_gauge("memory_usage_rss", 512).never
173
+ probe.call
174
+ end
175
+ end
176
+
177
+ context "when hostname is configured for probe" do
178
+ let(:redis_hostname) { "my_redis_server" }
179
+ let(:probe) { described_class.new(:hostname => redis_hostname) }
180
+
181
+ it "uses the redis hostname for the hostname tag" do
182
+ allow(Appsignal).to receive(:set_gauge).and_call_original
183
+ log = capture_logs { probe }
184
+ expect(log).to contains_log(
185
+ :debug,
186
+ %(Initializing Sidekiq probe with config: {:hostname=>"#{redis_hostname}"})
187
+ )
188
+ log = capture_logs { probe.call }
189
+ expect(log).to contains_log(
190
+ :debug,
191
+ "Sidekiq probe: Using hostname config option #{redis_hostname.inspect} as hostname"
192
+ )
193
+ expect(Appsignal).to have_received(:set_gauge)
194
+ .with(anything, anything, :hostname => redis_hostname).at_least(:once)
195
+ end
196
+ end
197
+
198
+ def expect_gauge(key, value, tags = {})
199
+ expect(Appsignal).to receive(:set_gauge)
200
+ .with("sidekiq_#{key}", value, expected_default_tags.merge(tags))
201
+ .and_call_original
202
+ end
203
+ end
204
+ end
@@ -4,9 +4,12 @@ describe Appsignal::Rack::JSExceptionCatcher do
4
4
  let(:config_options) { { :enable_frontend_error_catching => true } }
5
5
  let(:config) { project_fixture_config("production", config_options) }
6
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/"
7
+ "The Appsignal::Rack::JSExceptionCatcher is " \
8
+ "deprecated and will be removed in a future version. Please use " \
9
+ "the official AppSignal JavaScript integration by disabling " \
10
+ "`enable_frontend_error_catching` in your configuration and " \
11
+ "installing AppSignal for JavaScript instead. " \
12
+ "(https://docs.appsignal.com/front-end/)"
10
13
  end
11
14
  before { Appsignal.config = config }
12
15
 
@@ -32,7 +35,9 @@ describe Appsignal::Rack::JSExceptionCatcher do
32
35
 
33
36
  describe "#call" do
34
37
  let(:catcher) do
35
- silence { Appsignal::Rack::JSExceptionCatcher.new(app, options) }
38
+ silence :allowed => ["enable_frontend_error_catching"] do
39
+ Appsignal::Rack::JSExceptionCatcher.new(app, options)
40
+ end
36
41
  end
37
42
  after { catcher.call(env) }
38
43
 
@@ -473,22 +473,20 @@ describe Appsignal::Transaction do
473
473
  end
474
474
  end
475
475
 
476
- describe "set_queue_start" do
477
- it "should set the queue start in extension" do
478
- expect(transaction.ext).to receive(:set_queue_start).with(
479
- 10.0
480
- ).once
476
+ describe "#set_queue_start" do
477
+ it "sets the queue start in extension" do
478
+ expect(transaction.ext).to receive(:set_queue_start).with(10.0).once
481
479
 
482
480
  transaction.set_queue_start(10.0)
483
481
  end
484
482
 
485
- it "should not set the queue start in extension when value is nil" do
483
+ it "does not set the queue start in extension when value is nil" do
486
484
  expect(transaction.ext).to_not receive(:set_queue_start)
487
485
 
488
486
  transaction.set_queue_start(nil)
489
487
  end
490
488
 
491
- it "should not raise an error when the queue start is too big" do
489
+ it "does not raise an error when the queue start is too big" do
492
490
  expect(transaction.ext).to receive(:set_queue_start).and_raise(RangeError)
493
491
 
494
492
  expect(Appsignal.logger).to receive(:warn).with("Queue start value 10 is too big")
@@ -62,7 +62,7 @@ RSpec.describe "Puma plugin" do
62
62
  expect(launcher.events.on_booted).to_not be_nil
63
63
 
64
64
  launcher.events.on_booted.call
65
- expect(Appsignal::Minutely.probes[:puma]).to eql(Appsignal::Hooks::PumaProbe)
65
+ expect(Appsignal::Minutely.probes[:puma]).to eql(Appsignal::Probes::PumaProbe)
66
66
 
67
67
  # Minutely probes started and called
68
68
  wait_for("enough probe calls") { probe.calls >= 2 }
@@ -0,0 +1,25 @@
1
+ module ActionMailerHelpers
2
+ def perform_action_mailer(mailer, method, args = nil)
3
+ if DependencyHelper.rails_version >= Gem::Version.new("5.2.0")
4
+ case args
5
+ when Array
6
+ mailer.send(method, *args).deliver_later
7
+ when Hash
8
+ mailer.with(args).send(method).deliver_later
9
+ when NilClass
10
+ mailer.send(method).deliver_later
11
+ else
12
+ raise "Unknown scenario for arguments: #{args}"
13
+ end
14
+ else
15
+ # Rails 5.1 and lower
16
+ mailer_object =
17
+ if args
18
+ mailer.send(method, *args)
19
+ else
20
+ mailer.send(method)
21
+ end
22
+ mailer_object.deliver_later
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,10 @@
1
1
  module DependencyHelper
2
2
  module_function
3
3
 
4
+ def ruby_version
5
+ Gem::Version.new(RUBY_VERSION)
6
+ end
7
+
4
8
  def running_jruby?
5
9
  defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
6
10
  end
@@ -10,8 +14,11 @@ module DependencyHelper
10
14
  end
11
15
 
12
16
  def rails6_present?
13
- rails_present? &&
14
- Gem.loaded_specs["rails"].version >= Gem::Version.new("6.0.0")
17
+ rails_present? && rails_version >= Gem::Version.new("6.0.0")
18
+ end
19
+
20
+ def rails_version
21
+ Gem.loaded_specs["rails"].version
15
22
  end
16
23
 
17
24
  def sequel_present?
@@ -44,6 +44,12 @@ module TransactionHelpers
44
44
  created_transactions.last
45
45
  end
46
46
 
47
+ # Set current transaction manually.
48
+ # Cleared by {clear_current_transaction!}
49
+ def set_current_transaction(transaction) # rubocop:disable Style/AccessorMethodName
50
+ Thread.current[:appsignal_transaction] = transaction
51
+ end
52
+
47
53
  # Use when {Appsignal::Transaction.clear_current_transaction!} is stubbed to
48
54
  # clear the current transaction on the current thread.
49
55
  def clear_current_transaction!
@@ -1,4 +1,4 @@
1
- class Sidekiq
2
- class API
1
+ module Sidekiq
2
+ class Stats
3
3
  end
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.0.alpha.1
4
+ version: 2.11.0.beta.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-06-29 00:00:00.000000000 Z
13
+ date: 2020-07-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -172,7 +172,8 @@ files:
172
172
  - gemfiles/rails-5.1.gemfile
173
173
  - gemfiles/rails-5.2.gemfile
174
174
  - gemfiles/rails-6.0.gemfile
175
- - gemfiles/resque.gemfile
175
+ - gemfiles/resque-1.gemfile
176
+ - gemfiles/resque-2.gemfile
176
177
  - gemfiles/sequel-435.gemfile
177
178
  - gemfiles/sequel.gemfile
178
179
  - gemfiles/sinatra.gemfile
@@ -206,6 +207,7 @@ files:
206
207
  - lib/appsignal/helpers/metrics.rb
207
208
  - lib/appsignal/hooks.rb
208
209
  - lib/appsignal/hooks/action_cable.rb
210
+ - lib/appsignal/hooks/active_job.rb
209
211
  - lib/appsignal/hooks/active_support_notifications.rb
210
212
  - lib/appsignal/hooks/celluloid.rb
211
213
  - lib/appsignal/hooks/data_mapper.rb
@@ -217,6 +219,7 @@ files:
217
219
  - lib/appsignal/hooks/que.rb
218
220
  - lib/appsignal/hooks/rake.rb
219
221
  - lib/appsignal/hooks/redis.rb
222
+ - lib/appsignal/hooks/resque.rb
220
223
  - lib/appsignal/hooks/sequel.rb
221
224
  - lib/appsignal/hooks/shoryuken.rb
222
225
  - lib/appsignal/hooks/sidekiq.rb
@@ -241,6 +244,8 @@ files:
241
244
  - lib/appsignal/logger.rb
242
245
  - lib/appsignal/marker.rb
243
246
  - lib/appsignal/minutely.rb
247
+ - lib/appsignal/probes/puma.rb
248
+ - lib/appsignal/probes/sidekiq.rb
244
249
  - lib/appsignal/rack/generic_instrumentation.rb
245
250
  - lib/appsignal/rack/js_exception_catcher.rb
246
251
  - lib/appsignal/rack/rails_instrumentation.rb
@@ -289,6 +294,7 @@ files:
289
294
  - spec/lib/appsignal/garbage_collection_profiler_spec.rb
290
295
  - spec/lib/appsignal/hooks/action_cable_spec.rb
291
296
  - spec/lib/appsignal/hooks/active_support_notifications_spec.rb
297
+ - spec/lib/appsignal/hooks/activejob_spec.rb
292
298
  - spec/lib/appsignal/hooks/celluloid_spec.rb
293
299
  - spec/lib/appsignal/hooks/data_mapper_spec.rb
294
300
  - spec/lib/appsignal/hooks/delayed_job_spec.rb
@@ -299,6 +305,7 @@ files:
299
305
  - spec/lib/appsignal/hooks/que_spec.rb
300
306
  - spec/lib/appsignal/hooks/rake_spec.rb
301
307
  - spec/lib/appsignal/hooks/redis_spec.rb
308
+ - spec/lib/appsignal/hooks/resque_spec.rb
302
309
  - spec/lib/appsignal/hooks/sequel_spec.rb
303
310
  - spec/lib/appsignal/hooks/shoryuken_spec.rb
304
311
  - spec/lib/appsignal/hooks/sidekiq_spec.rb
@@ -320,6 +327,8 @@ files:
320
327
  - spec/lib/appsignal/logger_spec.rb
321
328
  - spec/lib/appsignal/marker_spec.rb
322
329
  - spec/lib/appsignal/minutely_spec.rb
330
+ - spec/lib/appsignal/probes/puma_spec.rb
331
+ - spec/lib/appsignal/probes/sidekiq_spec.rb
323
332
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
324
333
  - spec/lib/appsignal/rack/js_exception_catcher_spec.rb
325
334
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb
@@ -344,6 +353,7 @@ files:
344
353
  - spec/support/fixtures/projects/valid/config/environments/test.rb
345
354
  - spec/support/fixtures/projects/valid/log/.gitkeep
346
355
  - spec/support/fixtures/uploaded_file.txt
356
+ - spec/support/helpers/action_mailer_helpers.rb
347
357
  - spec/support/helpers/api_request_helper.rb
348
358
  - spec/support/helpers/cli_helpers.rb
349
359
  - spec/support/helpers/config_helpers.rb
@@ -431,6 +441,7 @@ test_files:
431
441
  - spec/lib/appsignal/garbage_collection_profiler_spec.rb
432
442
  - spec/lib/appsignal/hooks/action_cable_spec.rb
433
443
  - spec/lib/appsignal/hooks/active_support_notifications_spec.rb
444
+ - spec/lib/appsignal/hooks/activejob_spec.rb
434
445
  - spec/lib/appsignal/hooks/celluloid_spec.rb
435
446
  - spec/lib/appsignal/hooks/data_mapper_spec.rb
436
447
  - spec/lib/appsignal/hooks/delayed_job_spec.rb
@@ -441,6 +452,7 @@ test_files:
441
452
  - spec/lib/appsignal/hooks/que_spec.rb
442
453
  - spec/lib/appsignal/hooks/rake_spec.rb
443
454
  - spec/lib/appsignal/hooks/redis_spec.rb
455
+ - spec/lib/appsignal/hooks/resque_spec.rb
444
456
  - spec/lib/appsignal/hooks/sequel_spec.rb
445
457
  - spec/lib/appsignal/hooks/shoryuken_spec.rb
446
458
  - spec/lib/appsignal/hooks/sidekiq_spec.rb
@@ -462,6 +474,8 @@ test_files:
462
474
  - spec/lib/appsignal/logger_spec.rb
463
475
  - spec/lib/appsignal/marker_spec.rb
464
476
  - spec/lib/appsignal/minutely_spec.rb
477
+ - spec/lib/appsignal/probes/puma_spec.rb
478
+ - spec/lib/appsignal/probes/sidekiq_spec.rb
465
479
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
466
480
  - spec/lib/appsignal/rack/js_exception_catcher_spec.rb
467
481
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb
@@ -486,6 +500,7 @@ test_files:
486
500
  - spec/support/fixtures/projects/valid/config/environments/test.rb
487
501
  - spec/support/fixtures/projects/valid/log/.gitkeep
488
502
  - spec/support/fixtures/uploaded_file.txt
503
+ - spec/support/helpers/action_mailer_helpers.rb
489
504
  - spec/support/helpers/api_request_helper.rb
490
505
  - spec/support/helpers/cli_helpers.rb
491
506
  - spec/support/helpers/config_helpers.rb