appsignal 2.10.8-java → 2.11.0.beta.1-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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.semaphore/semaphore.yml +75 -61
- data/CHANGELOG.md +21 -0
- data/build_matrix.yml +13 -7
- data/ext/agent.yml +19 -19
- data/ext/appsignal_extension.c +10 -1
- data/ext/base.rb +11 -2
- data/gemfiles/padrino.gemfile +2 -2
- data/gemfiles/rails-4.2.gemfile +9 -2
- data/gemfiles/rails-5.0.gemfile +1 -0
- data/gemfiles/rails-5.1.gemfile +1 -0
- data/gemfiles/rails-5.2.gemfile +1 -0
- data/gemfiles/rails-6.0.gemfile +1 -0
- data/gemfiles/resque-1.gemfile +7 -0
- data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
- data/lib/appsignal.rb +21 -1
- data/lib/appsignal/capistrano.rb +2 -0
- data/lib/appsignal/config.rb +6 -2
- data/lib/appsignal/environment.rb +126 -0
- data/lib/appsignal/extension/jruby.rb +10 -0
- data/lib/appsignal/hooks.rb +2 -0
- data/lib/appsignal/hooks/active_job.rb +89 -0
- data/lib/appsignal/hooks/net_http.rb +2 -0
- data/lib/appsignal/hooks/puma.rb +2 -58
- data/lib/appsignal/hooks/redis.rb +2 -0
- data/lib/appsignal/hooks/resque.rb +60 -0
- data/lib/appsignal/hooks/sequel.rb +2 -0
- data/lib/appsignal/hooks/sidekiq.rb +18 -191
- data/lib/appsignal/integrations/object.rb +4 -0
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +9 -12
- data/lib/appsignal/integrations/resque_active_job.rb +9 -24
- data/lib/appsignal/probes/puma.rb +61 -0
- data/lib/appsignal/probes/sidekiq.rb +102 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +5 -2
- data/lib/appsignal/transaction.rb +32 -7
- data/lib/appsignal/utils/deprecation_message.rb +5 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +2 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +2 -1
- data/spec/lib/appsignal/config_spec.rb +6 -1
- data/spec/lib/appsignal/environment_spec.rb +167 -0
- data/spec/lib/appsignal/hooks/activejob_spec.rb +458 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +2 -181
- data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +292 -546
- data/spec/lib/appsignal/integrations/padrino_spec.rb +1 -1
- data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -137
- data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
- data/spec/lib/appsignal/probes/puma_spec.rb +180 -0
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +204 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +9 -4
- data/spec/lib/appsignal/transaction_spec.rb +35 -20
- data/spec/lib/appsignal_spec.rb +22 -0
- data/spec/lib/puma/appsignal_spec.rb +1 -1
- data/spec/support/helpers/action_mailer_helpers.rb +25 -0
- data/spec/support/helpers/dependency_helper.rb +12 -0
- data/spec/support/helpers/env_helpers.rb +1 -1
- data/spec/support/helpers/environment_metdata_helper.rb +16 -0
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- data/spec/support/stubs/sidekiq/api.rb +2 -2
- metadata +25 -5
@@ -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, "
|
145
|
+
expect(response).to match_response(404, "GET /404")
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
@@ -14,7 +14,6 @@ if DependencyHelper.que_present?
|
|
14
14
|
:error_count => 0
|
15
15
|
}
|
16
16
|
end
|
17
|
-
|
18
17
|
let(:env) do
|
19
18
|
{
|
20
19
|
:class => "MyQueJob",
|
@@ -29,7 +28,6 @@ if DependencyHelper.que_present?
|
|
29
28
|
:params => %w[1 birds]
|
30
29
|
}
|
31
30
|
end
|
32
|
-
|
33
31
|
let(:job) do
|
34
32
|
Class.new(::Que::Job) do
|
35
33
|
def run(*args)
|
@@ -37,7 +35,6 @@ if DependencyHelper.que_present?
|
|
37
35
|
end
|
38
36
|
end
|
39
37
|
let(:instance) { job.new(job_attrs) }
|
40
|
-
|
41
38
|
before do
|
42
39
|
allow(Que).to receive(:execute)
|
43
40
|
|
@@ -46,10 +43,14 @@ if DependencyHelper.que_present?
|
|
46
43
|
end
|
47
44
|
around { |example| keep_transactions { example.run } }
|
48
45
|
|
46
|
+
def perform_job(job)
|
47
|
+
job._run
|
48
|
+
end
|
49
|
+
|
49
50
|
context "success" do
|
50
51
|
it "creates a transaction for a job" do
|
51
52
|
expect do
|
52
|
-
instance
|
53
|
+
perform_job(instance)
|
53
54
|
end.to change { created_transactions.length }.by(1)
|
54
55
|
|
55
56
|
expect(last_transaction).to be_completed
|
@@ -95,7 +96,7 @@ if DependencyHelper.que_present?
|
|
95
96
|
|
96
97
|
expect do
|
97
98
|
expect do
|
98
|
-
instance
|
99
|
+
perform_job(instance)
|
99
100
|
end.to raise_error(ExampleException)
|
100
101
|
end.to change { created_transactions.length }.by(1)
|
101
102
|
|
@@ -130,7 +131,7 @@ if DependencyHelper.que_present?
|
|
130
131
|
it "reports errors and not re-raise them" do
|
131
132
|
allow(instance).to receive(:run).and_raise(error)
|
132
133
|
|
133
|
-
expect { instance
|
134
|
+
expect { perform_job(instance) }.to change { created_transactions.length }.by(1)
|
134
135
|
|
135
136
|
expect(last_transaction).to be_completed
|
136
137
|
transaction_hash = last_transaction.to_h
|
@@ -156,6 +157,24 @@ if DependencyHelper.que_present?
|
|
156
157
|
)
|
157
158
|
end
|
158
159
|
end
|
160
|
+
|
161
|
+
context "when action set in job" do
|
162
|
+
let(:job) do
|
163
|
+
Class.new(::Que::Job) do
|
164
|
+
def run(*_args)
|
165
|
+
Appsignal.set_action("MyCustomJob#perform")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it "uses the custom action" do
|
171
|
+
perform_job(instance)
|
172
|
+
|
173
|
+
expect(last_transaction).to be_completed
|
174
|
+
transaction_hash = last_transaction.to_h
|
175
|
+
expect(transaction_hash).to include("action" => "MyCustomJob#perform")
|
176
|
+
end
|
177
|
+
end
|
159
178
|
end
|
160
179
|
end
|
161
180
|
end
|
@@ -1,145 +1,28 @@
|
|
1
|
-
|
2
|
-
require "active_job"
|
1
|
+
require "appsignal/integrations/resque_active_job"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
load file
|
10
|
-
start_agent
|
3
|
+
describe "Legacy Resque ActiveJob integration" do
|
4
|
+
let(:err_stream) { std_stream }
|
5
|
+
let(:stderr) { err_stream.read }
|
6
|
+
let(:log_stream) { std_stream }
|
7
|
+
let(:log) { log_contents(log_stream) }
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def perform(_)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def perform
|
21
|
-
keep_transactions do
|
22
|
-
job.perform_now
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "without error" do
|
27
|
-
it "creates a new transaction" do
|
28
|
-
expect { perform }.to change { created_transactions.length }.by(1)
|
29
|
-
|
30
|
-
expect(last_transaction.to_h).to include(
|
31
|
-
"namespace" => Appsignal::Transaction::BACKGROUND_JOB,
|
32
|
-
"action" => "TestActiveJob#perform",
|
33
|
-
"error" => nil,
|
34
|
-
"events" => [
|
35
|
-
hash_including(
|
36
|
-
"name" => "perform_job.resque",
|
37
|
-
"title" => "",
|
38
|
-
"body" => "",
|
39
|
-
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
40
|
-
"count" => 1,
|
41
|
-
"duration" => kind_of(Float)
|
42
|
-
)
|
43
|
-
],
|
44
|
-
"sample_data" => hash_including(
|
45
|
-
"params" => ["argument"],
|
46
|
-
"metadata" => {
|
47
|
-
"id" => kind_of(String),
|
48
|
-
"queue" => "default"
|
49
|
-
}
|
50
|
-
)
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context "with error" do
|
56
|
-
let(:job) do
|
57
|
-
class BrokenTestActiveJob < ActiveJob::Base
|
58
|
-
include Appsignal::Integrations::ResqueActiveJobPlugin
|
9
|
+
it "logs and prints a deprecation message on extend" do
|
10
|
+
Appsignal.logger = test_logger(log_stream)
|
59
11
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
BrokenTestActiveJob.new(args)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "creates a new transaction with an error" do
|
69
|
-
expect do
|
70
|
-
expect { perform }.to raise_error(ExampleException, "my error message")
|
71
|
-
end.to change { created_transactions.length }.by(1)
|
72
|
-
|
73
|
-
expect(last_transaction.to_h).to include(
|
74
|
-
"namespace" => Appsignal::Transaction::BACKGROUND_JOB,
|
75
|
-
"action" => "BrokenTestActiveJob#perform",
|
76
|
-
"error" => {
|
77
|
-
"name" => "ExampleException",
|
78
|
-
"message" => "my error message",
|
79
|
-
"backtrace" => kind_of(String)
|
80
|
-
},
|
81
|
-
"sample_data" => hash_including(
|
82
|
-
"params" => ["argument"],
|
83
|
-
"metadata" => {
|
84
|
-
"id" => kind_of(String),
|
85
|
-
"queue" => "default"
|
86
|
-
}
|
87
|
-
)
|
88
|
-
)
|
12
|
+
capture_std_streams(std_stream, err_stream) do
|
13
|
+
Class.new do
|
14
|
+
include Appsignal::Integrations::ResqueActiveJobPlugin
|
89
15
|
end
|
90
16
|
end
|
91
17
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
it "truncates large argument values" do
|
102
|
-
perform
|
103
|
-
expect(last_transaction.to_h).to include(
|
104
|
-
"namespace" => Appsignal::Transaction::BACKGROUND_JOB,
|
105
|
-
"action" => "TestActiveJob#perform",
|
106
|
-
"error" => nil,
|
107
|
-
"sample_data" => hash_including(
|
108
|
-
"params" => ["foo" => "Foo", "bar" => "#{"a" * 2000}..."],
|
109
|
-
"metadata" => {
|
110
|
-
"id" => kind_of(String),
|
111
|
-
"queue" => "default"
|
112
|
-
}
|
113
|
-
)
|
114
|
-
)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context "with parameter filtering" do
|
119
|
-
let(:args) do
|
120
|
-
{
|
121
|
-
:foo => "Foo",
|
122
|
-
:bar => "Bar"
|
123
|
-
}
|
124
|
-
end
|
125
|
-
before { Appsignal.config[:filter_parameters] = ["foo"] }
|
126
|
-
|
127
|
-
it "filters selected arguments" do
|
128
|
-
perform
|
129
|
-
expect(last_transaction.to_h).to include(
|
130
|
-
"namespace" => Appsignal::Transaction::BACKGROUND_JOB,
|
131
|
-
"action" => "TestActiveJob#perform",
|
132
|
-
"error" => nil,
|
133
|
-
"sample_data" => hash_including(
|
134
|
-
"params" => ["foo" => "[FILTERED]", "bar" => "Bar"],
|
135
|
-
"metadata" => {
|
136
|
-
"id" => kind_of(String),
|
137
|
-
"queue" => "default"
|
138
|
-
}
|
139
|
-
)
|
140
|
-
)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
18
|
+
deprecation_message =
|
19
|
+
"The AppSignal ResqueActiveJobPlugin is deprecated and does " \
|
20
|
+
"nothing on extend. In this version of the AppSignal Ruby gem " \
|
21
|
+
"the integration with Resque is automatic on all Resque workers. " \
|
22
|
+
"Please remove the following line from this file to remove this " \
|
23
|
+
"message: include Appsignal::Integrations::ResqueActiveJobPlugin\n" \
|
24
|
+
"#{__FILE__}:"
|
25
|
+
expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
|
26
|
+
expect(log).to contains_log :warn, deprecation_message
|
144
27
|
end
|
145
28
|
end
|
@@ -1,93 +1,28 @@
|
|
1
|
-
|
2
|
-
describe "Resque integration" do
|
3
|
-
let(:file) { File.expand_path("lib/appsignal/integrations/resque.rb") }
|
1
|
+
require "appsignal/integrations/resque"
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
describe "Legacy Resque integration" do
|
4
|
+
let(:err_stream) { std_stream }
|
5
|
+
let(:stderr) { err_stream.read }
|
6
|
+
let(:log_stream) { std_stream }
|
7
|
+
let(:log) { log_contents(log_stream) }
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
it "logs and prints a deprecation message on extend" do
|
10
|
+
Appsignal.logger = test_logger(log_stream)
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
class BrokenTestJob
|
18
|
-
extend Appsignal::Integrations::ResquePlugin
|
19
|
-
|
20
|
-
def self.perform
|
21
|
-
raise ExampleException, "my error message"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe :around_perform_resque_plugin do
|
27
|
-
let(:job) { ::Resque::Job.new("default", "class" => "TestJob") }
|
28
|
-
before { expect(Appsignal).to receive(:stop) }
|
29
|
-
|
30
|
-
context "without exception" do
|
31
|
-
it "creates a new transaction" do
|
32
|
-
expect do
|
33
|
-
keep_transactions { job.perform }
|
34
|
-
end.to change { created_transactions.length }.by(1)
|
35
|
-
|
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
|
-
]
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context "with exception" do
|
56
|
-
let(:job) { ::Resque::Job.new("default", "class" => "BrokenTestJob") }
|
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
|
64
|
-
end
|
65
|
-
|
66
|
-
it "sets the exception on the transaction" do
|
67
|
-
expect do
|
68
|
-
perform
|
69
|
-
end.to change { created_transactions.length }.by(1)
|
70
|
-
|
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
|
-
)
|
81
|
-
end
|
82
|
-
end
|
12
|
+
capture_std_streams(std_stream, err_stream) do
|
13
|
+
Class.new do
|
14
|
+
extend Appsignal::Integrations::ResquePlugin
|
83
15
|
end
|
84
16
|
end
|
85
17
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
18
|
+
deprecation_message =
|
19
|
+
"The AppSignal ResquePlugin is deprecated and does " \
|
20
|
+
"nothing on extend. In this version of the AppSignal Ruby gem " \
|
21
|
+
"the integration with Resque is automatic on all Resque workers. " \
|
22
|
+
"Please remove the following line from this file to remove this " \
|
23
|
+
"message: extend Appsignal::Integrations::ResquePlugin\n" \
|
24
|
+
"#{__FILE__}:"
|
25
|
+
expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
|
26
|
+
expect(log).to contains_log :warn, deprecation_message
|
92
27
|
end
|
93
28
|
end
|
@@ -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
|