appsignal 2.11.0.alpha.1-java → 2.11.0.alpha.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.
@@ -142,7 +142,7 @@ if DependencyHelper.padrino_present?
142
142
  expect_a_transaction_to_be_created
143
143
  # Uses path for action name
144
144
  expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp#unknown")
145
- expect(response).to match_response(404, "<h1>Not Found</h1>")
145
+ expect(response).to match_response(404, "GET /404")
146
146
  end
147
147
  end
148
148
 
@@ -0,0 +1,180 @@
1
+ require "appsignal/probes/puma"
2
+
3
+ describe Appsignal::Probes::PumaProbe do
4
+ before(:context) do
5
+ Appsignal.config = project_fixture_config
6
+ end
7
+ after(:context) do
8
+ Appsignal.config = nil
9
+ end
10
+
11
+ let(:probe) { described_class.new }
12
+
13
+ describe "hostname" do
14
+ it "returns the socket hostname" do
15
+ expect(probe.send(:hostname)).to eql(Socket.gethostname)
16
+ end
17
+
18
+ context "with overridden hostname" do
19
+ around do |sample|
20
+ Appsignal.config[:hostname] = "frontend1"
21
+ sample.run
22
+ Appsignal.config[:hostname] = nil
23
+ end
24
+ it "returns the configured host" do
25
+ expect(probe.send(:hostname)).to eql("frontend1")
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#call" do
31
+ let(:expected_default_tags) { { :hostname => Socket.gethostname } }
32
+
33
+ context "with multiple worker stats" do
34
+ before(:context) do
35
+ class Puma
36
+ def self.stats
37
+ {
38
+ "workers" => 2,
39
+ "booted_workers" => 2,
40
+ "old_workers" => 0,
41
+ "worker_status" => [
42
+ {
43
+ "last_status" => {
44
+ "backlog" => 0,
45
+ "running" => 5,
46
+ "pool_capacity" => 5,
47
+ "max_threads" => 5
48
+ }
49
+ },
50
+ {
51
+ "last_status" => {
52
+ "backlog" => 0,
53
+ "running" => 5,
54
+ "pool_capacity" => 5,
55
+ "max_threads" => 5
56
+ }
57
+ }
58
+ ]
59
+ }.to_json
60
+ end
61
+ end
62
+ end
63
+ after(:context) { Object.send(:remove_const, :Puma) }
64
+
65
+ it "calls `puma_gauge` with the (summed) worker metrics" do
66
+ expect_gauge(:workers, 2, :type => :count)
67
+ expect_gauge(:workers, 2, :type => :booted)
68
+ expect_gauge(:workers, 0, :type => :old)
69
+
70
+ expect_gauge(:connection_backlog, 0)
71
+ expect_gauge(:pool_capacity, 10)
72
+ expect_gauge(:threads, 10, :type => :running)
73
+ expect_gauge(:threads, 10, :type => :max)
74
+
75
+ probe.call
76
+ end
77
+ end
78
+
79
+ context "with single worker stats" do
80
+ before(:context) do
81
+ class Puma
82
+ def self.stats
83
+ {
84
+ "backlog" => 0,
85
+ "running" => 5,
86
+ "pool_capacity" => 5,
87
+ "max_threads" => 5
88
+ }.to_json
89
+ end
90
+ end
91
+ end
92
+ after(:context) { Object.send(:remove_const, :Puma) }
93
+
94
+ it "calls `puma_gauge` with the (summed) worker metrics" do
95
+ expect_gauge(:connection_backlog, 0)
96
+ expect_gauge(:pool_capacity, 5)
97
+ expect_gauge(:threads, 5, :type => :running)
98
+ expect_gauge(:threads, 5, :type => :max)
99
+ probe.call
100
+ end
101
+ end
102
+
103
+ context "without stats" do
104
+ before(:context) do
105
+ class Puma
106
+ def self.stats
107
+ end
108
+ end
109
+ end
110
+ after(:context) { Object.send(:remove_const, :Puma) }
111
+
112
+ context "when it returns nil" do
113
+ it "does not track metrics" do
114
+ expect(probe).to_not receive(:puma_gauge)
115
+ probe.call
116
+ end
117
+ end
118
+
119
+ # Puma.stats raises a NoMethodError on a nil object on the first call.
120
+ context "when it returns a NoMethodError on the first call" do
121
+ let(:log) { StringIO.new }
122
+
123
+ it "ignores the first call and tracks the second call" do
124
+ use_logger_with log do
125
+ expect(Puma).to receive(:stats)
126
+ .and_raise(NoMethodError.new("undefined method `stats' for nil:NilClass"))
127
+ probe.call
128
+
129
+ expect(Puma).to receive(:stats).and_return({
130
+ "backlog" => 1,
131
+ "running" => 5,
132
+ "pool_capacity" => 4,
133
+ "max_threads" => 6
134
+ }.to_json)
135
+
136
+ expect_gauge(:connection_backlog, 1)
137
+ expect_gauge(:pool_capacity, 4)
138
+ expect_gauge(:threads, 5, :type => :running)
139
+ expect_gauge(:threads, 6, :type => :max)
140
+ probe.call
141
+ end
142
+
143
+ expect(log_contents(log)).to_not contains_log(:error, "Error in minutely probe 'puma'")
144
+ end
145
+ end
146
+
147
+ context "when it does not have a complete stats payload" do
148
+ let(:log) { StringIO.new }
149
+
150
+ it "tracks whatever metrics we do have" do
151
+ use_logger_with log do
152
+ expect(Puma).to receive(:stats).and_return({
153
+ "backlog" => 1,
154
+ "running" => 5
155
+ }.to_json)
156
+
157
+ expect_gauge(:connection_backlog, 1)
158
+ expect_no_gauge(:pool_capacity)
159
+ expect_gauge(:threads, 5, :type => :running)
160
+ expect_no_gauge(:threads, :type => :max)
161
+ probe.call
162
+ end
163
+
164
+ expect(log_contents(log)).to_not contains_log(:error, "Error in minutely probe 'puma'")
165
+ end
166
+ end
167
+ end
168
+
169
+ def expect_gauge(key, value, tags = {})
170
+ expect(Appsignal).to receive(:set_gauge)
171
+ .with("puma_#{key}", value, expected_default_tags.merge(tags))
172
+ .and_call_original
173
+ end
174
+
175
+ def expect_no_gauge(key, tags = {})
176
+ expect(Appsignal).to_not receive(:set_gauge)
177
+ .with("puma_#{key}", anything, tags)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,201 @@
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 Sidekiq
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
+ end
92
+ after { Object.send(:remove_const, "Sidekiq") }
93
+
94
+ describe ".dependencies_present?" do
95
+ before do
96
+ class Redis; end
97
+ Redis.const_set(:VERSION, version)
98
+ end
99
+ after { Object.send(:remove_const, "Redis") }
100
+
101
+ context "when Redis version is < 3.3.5" do
102
+ let(:version) { "3.3.4" }
103
+
104
+ it "does not start probe" do
105
+ expect(described_class.dependencies_present?).to be_falsy
106
+ end
107
+ end
108
+
109
+ context "when Redis version is >= 3.3.5" do
110
+ let(:version) { "3.3.5" }
111
+
112
+ it "does not start probe" do
113
+ expect(described_class.dependencies_present?).to be_truthy
114
+ end
115
+ end
116
+ end
117
+
118
+ it "loads Sidekiq::API" do
119
+ expect(defined?(Sidekiq::API)).to be_falsy
120
+ probe
121
+ expect(defined?(Sidekiq::API)).to be_truthy
122
+ end
123
+
124
+ it "logs config on initialize" do
125
+ log = capture_logs { probe }
126
+ expect(log).to contains_log(:debug, "Initializing Sidekiq probe\n")
127
+ end
128
+
129
+ it "logs used hostname on call once" do
130
+ log = capture_logs { probe.call }
131
+ expect(log).to contains_log(
132
+ :debug,
133
+ %(Sidekiq probe: Using Redis server hostname "localhost" as hostname)
134
+ )
135
+ log = capture_logs { probe.call }
136
+ # Match more logs with incompelete message
137
+ expect(log).to_not contains_log(:debug, %(Sidekiq probe: ))
138
+ end
139
+
140
+ it "collects custom metrics" do
141
+ expect_gauge("worker_count", 24).twice
142
+ expect_gauge("process_count", 25).twice
143
+ expect_gauge("connection_count", 2).twice
144
+ expect_gauge("memory_usage", 1024).twice
145
+ expect_gauge("memory_usage_rss", 512).twice
146
+ expect_gauge("job_count", 5, :status => :processed) # Gauge delta
147
+ expect_gauge("job_count", 3, :status => :failed) # Gauge delta
148
+ expect_gauge("job_count", 12, :status => :retry_queue).twice
149
+ expect_gauge("job_count", 2, :status => :died) # Gauge delta
150
+ expect_gauge("job_count", 14, :status => :scheduled).twice
151
+ expect_gauge("job_count", 15, :status => :enqueued).twice
152
+ expect_gauge("queue_length", 10, :queue => "default").twice
153
+ expect_gauge("queue_latency", 12_000, :queue => "default").twice
154
+ expect_gauge("queue_length", 1, :queue => "critical").twice
155
+ expect_gauge("queue_latency", 2_000, :queue => "critical").twice
156
+ # Call probe twice so we can calculate the delta for some gauge values
157
+ probe.call
158
+ probe.call
159
+ end
160
+
161
+ context "when `redis_info` is not defined" do
162
+ before do
163
+ allow(Sidekiq).to receive(:respond_to?).with(:redis_info).and_return(false)
164
+ end
165
+
166
+ it "does not collect redis metrics" do
167
+ expect_gauge("connection_count", 2).never
168
+ expect_gauge("memory_usage", 1024).never
169
+ expect_gauge("memory_usage_rss", 512).never
170
+ probe.call
171
+ end
172
+ end
173
+
174
+ context "when hostname is configured for probe" do
175
+ let(:redis_hostname) { "my_redis_server" }
176
+ let(:probe) { described_class.new(:hostname => redis_hostname) }
177
+
178
+ it "uses the redis hostname for the hostname tag" do
179
+ allow(Appsignal).to receive(:set_gauge).and_call_original
180
+ log = capture_logs { probe }
181
+ expect(log).to contains_log(
182
+ :debug,
183
+ %(Initializing Sidekiq probe with config: {:hostname=>"#{redis_hostname}"})
184
+ )
185
+ log = capture_logs { probe.call }
186
+ expect(log).to contains_log(
187
+ :debug,
188
+ "Sidekiq probe: Using hostname config option #{redis_hostname.inspect} as hostname"
189
+ )
190
+ expect(Appsignal).to have_received(:set_gauge)
191
+ .with(anything, anything, :hostname => redis_hostname).at_least(:once)
192
+ end
193
+ end
194
+
195
+ def expect_gauge(key, value, tags = {})
196
+ expect(Appsignal).to receive(:set_gauge)
197
+ .with("sidekiq_#{key}", value, expected_default_tags.merge(tags))
198
+ .and_call_original
199
+ end
200
+ end
201
+ 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
 
@@ -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 }
@@ -1,4 +1,4 @@
1
- class Sidekiq
1
+ module Sidekiq
2
2
  class API
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.alpha.2
5
5
  platform: java
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-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -255,6 +255,8 @@ files:
255
255
  - lib/appsignal/logger.rb
256
256
  - lib/appsignal/marker.rb
257
257
  - lib/appsignal/minutely.rb
258
+ - lib/appsignal/probes/puma.rb
259
+ - lib/appsignal/probes/sidekiq.rb
258
260
  - lib/appsignal/rack/generic_instrumentation.rb
259
261
  - lib/appsignal/rack/js_exception_catcher.rb
260
262
  - lib/appsignal/rack/rails_instrumentation.rb
@@ -334,6 +336,8 @@ files:
334
336
  - spec/lib/appsignal/logger_spec.rb
335
337
  - spec/lib/appsignal/marker_spec.rb
336
338
  - spec/lib/appsignal/minutely_spec.rb
339
+ - spec/lib/appsignal/probes/puma_spec.rb
340
+ - spec/lib/appsignal/probes/sidekiq_spec.rb
337
341
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
338
342
  - spec/lib/appsignal/rack/js_exception_catcher_spec.rb
339
343
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb
@@ -476,6 +480,8 @@ test_files:
476
480
  - spec/lib/appsignal/logger_spec.rb
477
481
  - spec/lib/appsignal/marker_spec.rb
478
482
  - spec/lib/appsignal/minutely_spec.rb
483
+ - spec/lib/appsignal/probes/puma_spec.rb
484
+ - spec/lib/appsignal/probes/sidekiq_spec.rb
479
485
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
480
486
  - spec/lib/appsignal/rack/js_exception_catcher_spec.rb
481
487
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb