appsignal 3.0.6 → 3.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/.rubocop.yml +2 -2
- data/.semaphore/semaphore.yml +460 -39
- data/CHANGELOG.md +23 -0
- data/README.md +22 -16
- data/Rakefile +11 -10
- data/build_matrix.yml +106 -32
- data/ext/agent.yml +24 -17
- data/ext/base.rb +7 -5
- data/lib/appsignal/cli/diagnose.rb +10 -5
- data/lib/appsignal/config.rb +1 -0
- data/lib/appsignal/hooks/puma.rb +1 -16
- data/lib/appsignal/probes.rb +0 -1
- data/lib/appsignal/system.rb +30 -3
- data/lib/appsignal/transaction.rb +1 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +146 -17
- data/script/install_lintje +18 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +4 -2
- data/spec/lib/appsignal/hooks/puma_spec.rb +0 -46
- data/spec/lib/appsignal/system_spec.rb +30 -0
- data/spec/lib/appsignal/transaction_spec.rb +7 -0
- data/spec/lib/puma/appsignal_spec.rb +246 -66
- data/spec/support/helpers/wait_for_helper.rb +16 -4
- metadata +4 -5
- data/lib/appsignal/probes/puma.rb +0 -61
- data/spec/lib/appsignal/probes/puma_spec.rb +0 -180
@@ -43,6 +43,14 @@ describe Appsignal::System do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
context "when using the APPSIGNAL_BUILD_FOR_LINUX_ARM env var" do
|
47
|
+
it "returns the linux build" do
|
48
|
+
ENV["APPSIGNAL_BUILD_FOR_LINUX_ARM"] = "1"
|
49
|
+
is_expected.to eq("linux")
|
50
|
+
ENV.delete("APPSIGNAL_BUILD_FOR_LINUX_ARM")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
46
54
|
context "when on a musl system" do
|
47
55
|
let(:ldd_output) { "musl libc (x86_64)\nVersion 1.1.16" }
|
48
56
|
|
@@ -93,4 +101,26 @@ describe Appsignal::System do
|
|
93
101
|
end
|
94
102
|
end
|
95
103
|
end
|
104
|
+
|
105
|
+
describe ".agent_architecture" do
|
106
|
+
let(:architecture) { "x86_64" }
|
107
|
+
let(:ldd_output) { "" }
|
108
|
+
before do
|
109
|
+
allow(RbConfig::CONFIG).to receive(:[])
|
110
|
+
allow(RbConfig::CONFIG).to receive(:[]).with("host_cpu").and_return(architecture)
|
111
|
+
end
|
112
|
+
subject { described_class.agent_architecture }
|
113
|
+
|
114
|
+
it "returns the host CPU value" do
|
115
|
+
is_expected.to eq(architecture)
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when using the APPSIGNAL_BUILD_FOR_LINUX_ARM env var" do
|
119
|
+
it "returns ARM 64 bit" do
|
120
|
+
ENV["APPSIGNAL_BUILD_FOR_LINUX_ARM"] = "1"
|
121
|
+
is_expected.to eq("aarch64")
|
122
|
+
ENV.delete("APPSIGNAL_BUILD_FOR_LINUX_ARM")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
96
126
|
end
|
@@ -1321,6 +1321,13 @@ describe Appsignal::Transaction do
|
|
1321
1321
|
expect(subject).to eq ["line 1", "line 2"]
|
1322
1322
|
end
|
1323
1323
|
|
1324
|
+
context "with Rails module but without backtrace_cleaner method" do
|
1325
|
+
it "returns the backtrace uncleaned" do
|
1326
|
+
stub_const("Rails", Module.new)
|
1327
|
+
expect(subject).to eq ["line 1", "line 2"]
|
1328
|
+
end
|
1329
|
+
end
|
1330
|
+
|
1324
1331
|
if rails_present?
|
1325
1332
|
context "with rails" do
|
1326
1333
|
it "cleans the backtrace with the Rails backtrace cleaner" do
|
@@ -10,110 +10,290 @@ RSpec.describe "Puma plugin" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
class MockPumaEvents
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
attr_reader :logs
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@logs = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def log(message)
|
20
|
+
@logs << [:log, message]
|
21
|
+
end
|
22
|
+
|
23
|
+
def debug(message)
|
24
|
+
@logs << [:debug, message]
|
25
|
+
end
|
26
|
+
|
27
|
+
def error(message)
|
28
|
+
@logs << [:error, message]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# StatsD server used for these tests.
|
33
|
+
# Open a UDPSocket and listen for messages sent by the AppSignal Puma plugin.
|
34
|
+
class StatsdServer
|
35
|
+
def start
|
36
|
+
stop
|
37
|
+
@socket = UDPSocket.new
|
38
|
+
@socket.bind("127.0.0.1", 8125)
|
39
|
+
|
40
|
+
loop do
|
41
|
+
begin
|
42
|
+
# Listen for messages and track them on the messages Array.
|
43
|
+
packet = @socket.recvfrom(1024)
|
44
|
+
track_message packet.first
|
45
|
+
rescue Errno::EBADF # rubocop:disable Lint/HandleExceptions
|
46
|
+
# Ignore error for JRuby 9.1.17.0 specifically, it doesn't appear to
|
47
|
+
# happen on 9.2.18.0. It doesn't break the tests themselves, ignoring
|
48
|
+
# this error. It's probably a timing issue where it tries to read
|
49
|
+
# from the socket after it's closed.
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
defined?(@socket) && @socket && @socket.close
|
56
|
+
ensure
|
57
|
+
@socket = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def messages
|
61
|
+
@messages ||= []
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def track_message(message)
|
67
|
+
@messages_mutex ||= Mutex.new
|
68
|
+
@messages_mutex.synchronize { messages << message }
|
16
69
|
end
|
17
70
|
end
|
18
71
|
|
19
72
|
let(:probe) { MockProbe.new }
|
20
73
|
let(:launcher) { MockPumaLauncher.new }
|
74
|
+
let(:hostname) { Socket.gethostname }
|
75
|
+
let(:expected_default_tags) { { "hostname" => hostname } }
|
76
|
+
let(:stats_data) { { :backlog => 1 } }
|
77
|
+
before :context do
|
78
|
+
Appsignal.stop
|
79
|
+
end
|
21
80
|
before do
|
22
81
|
module Puma
|
23
82
|
def self.stats
|
83
|
+
JSON.dump(@_stats_data)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.stats_hash
|
87
|
+
@_stats_data
|
24
88
|
end
|
25
89
|
|
26
|
-
def self.
|
27
|
-
|
28
|
-
before = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
29
|
-
|
30
|
-
# An abbreviated version of what happens in Puma::Cluster#run
|
31
|
-
launcher = MockPumaLauncher.new
|
32
|
-
plugin = Plugin.plugin.new
|
33
|
-
plugin.start(launcher)
|
34
|
-
launcher.events.on_booted.call
|
35
|
-
|
36
|
-
# Wait for minutely probe thread to finish starting
|
37
|
-
sleep 0.005
|
38
|
-
|
39
|
-
# Capture any new threads running after application is preloaded.
|
40
|
-
# Any threads created during the preloading phase will not be
|
41
|
-
# carried over into the forked workers. Puma warns about these
|
42
|
-
# but the minutely probe thread should only exist in the main process.
|
43
|
-
after = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
44
|
-
$stdout.puts "! WARNING: Detected #{after.size - before.size} Thread(s) started in app boot" if after.size > before.size
|
90
|
+
def self._set_stats=(data)
|
91
|
+
@_stats_data = data
|
45
92
|
end
|
46
93
|
|
47
94
|
class Plugin
|
48
95
|
class << self
|
49
|
-
attr_reader :
|
96
|
+
attr_reader :appsignal_plugin
|
50
97
|
|
51
98
|
def create(&block)
|
52
|
-
@
|
53
|
-
@
|
99
|
+
@appsignal_plugin = Class.new(::Puma::Plugin)
|
100
|
+
@appsignal_plugin.class_eval(&block)
|
54
101
|
end
|
55
102
|
end
|
56
|
-
end
|
57
|
-
end
|
58
103
|
|
59
|
-
|
60
|
-
ENV["APPSIGNAL_ENABLE_MINUTELY_PROBES"] = "true"
|
61
|
-
Appsignal.config = project_fixture_config
|
62
|
-
# Speed up test time
|
63
|
-
allow(Appsignal::Minutely).to receive(:initial_wait_time).and_return(0.001)
|
64
|
-
allow(Appsignal::Minutely).to receive(:wait_time).and_return(0.001)
|
104
|
+
attr_reader :in_background_block
|
65
105
|
|
66
|
-
|
106
|
+
def in_background(&block)
|
107
|
+
@in_background_block = block
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
Puma._set_stats = stats_data
|
67
112
|
load File.expand_path("../lib/puma/plugin/appsignal.rb", APPSIGNAL_SPEC_DIR)
|
113
|
+
|
114
|
+
@statsd = StatsdServer.new
|
115
|
+
@server_thread = Thread.new { @statsd.start }
|
116
|
+
@server_thread.abort_on_exception = true
|
68
117
|
end
|
69
118
|
after do
|
70
|
-
|
71
|
-
|
72
|
-
Object.send
|
119
|
+
@statsd = nil
|
120
|
+
|
121
|
+
Object.send(:remove_const, :Puma)
|
122
|
+
Object.send(:remove_const, :AppsignalPumaPlugin)
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_plugin(plugin, &block)
|
126
|
+
@client_thread = Thread.new { start_plugin(plugin) }
|
127
|
+
@client_thread.abort_on_exception = true
|
128
|
+
wait_for(:puma_client_wait, &block)
|
129
|
+
ensure
|
130
|
+
stop_all
|
73
131
|
end
|
74
132
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
plugin = Puma::Plugin.plugin.new
|
79
|
-
expect(launcher.events.on_booted).to be_nil
|
133
|
+
def appsignal_plugin
|
134
|
+
Puma::Plugin.appsignal_plugin
|
135
|
+
end
|
80
136
|
|
137
|
+
def start_plugin(plugin_class)
|
138
|
+
plugin = plugin_class.new
|
139
|
+
# Speed up test by not waiting for 60 seconds initial wait time and loop
|
140
|
+
# interval.
|
141
|
+
allow(plugin).to receive(:sleep_time).and_return(0.01)
|
81
142
|
plugin.start(launcher)
|
82
|
-
|
83
|
-
|
143
|
+
plugin.in_background_block.call
|
144
|
+
end
|
145
|
+
|
146
|
+
# Stop all threads in test and stop listening on the UDPSocket
|
147
|
+
def stop_all
|
148
|
+
@client_thread.kill if defined?(@client_thread) && @client_thread
|
149
|
+
@server_thread.kill if defined?(@server_thread) && @server_thread
|
150
|
+
@statsd.stop if defined?(@statsd) && @statsd
|
151
|
+
@client_thread = nil
|
152
|
+
@server_thread = nil
|
153
|
+
end
|
154
|
+
|
155
|
+
def logs
|
156
|
+
launcher.events.logs
|
157
|
+
end
|
158
|
+
|
159
|
+
def messages
|
160
|
+
@statsd.messages.map do |message|
|
161
|
+
metric, type, tags_string = message.split("|")
|
162
|
+
metric_name, metric_value = metric.split(":")
|
163
|
+
tags = {}
|
164
|
+
tags_string[1..-1].split(",").each do |tag|
|
165
|
+
key, value = tag.split(":")
|
166
|
+
tags[key] = value
|
167
|
+
end
|
168
|
+
{
|
169
|
+
:name => metric_name,
|
170
|
+
:value => metric_value.to_i,
|
171
|
+
:type => type,
|
172
|
+
:tags => tags
|
173
|
+
}
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def expect_gauge(metric_name, metric_value, tags_hash = {})
|
178
|
+
expect(messages).to include(
|
179
|
+
:name => "puma_#{metric_name}",
|
180
|
+
:value => metric_value,
|
181
|
+
:type => "g",
|
182
|
+
:tags => expected_default_tags.merge(tags_hash)
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
context "with multiple worker stats" do
|
187
|
+
let(:stats_data) do
|
188
|
+
{
|
189
|
+
:workers => 2,
|
190
|
+
:booted_workers => 2,
|
191
|
+
:old_workers => 0,
|
192
|
+
:worker_status => [
|
193
|
+
{
|
194
|
+
:last_status => {
|
195
|
+
:backlog => 0,
|
196
|
+
:running => 5,
|
197
|
+
:pool_capacity => 5,
|
198
|
+
:max_threads => 5
|
199
|
+
}
|
200
|
+
},
|
201
|
+
{
|
202
|
+
:last_status => {
|
203
|
+
:backlog => 0,
|
204
|
+
:running => 5,
|
205
|
+
:pool_capacity => 5,
|
206
|
+
:max_threads => 5
|
207
|
+
}
|
208
|
+
}
|
209
|
+
]
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
it "collects puma stats as guage metrics with the (summed) worker metrics" do
|
214
|
+
run_plugin(appsignal_plugin) do
|
215
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
216
|
+
expect_gauge(:workers, 2, "type" => "count")
|
217
|
+
expect_gauge(:workers, 2, "type" => "booted")
|
218
|
+
expect_gauge(:workers, 0, "type" => "old")
|
219
|
+
expect_gauge(:connection_backlog, 0)
|
220
|
+
expect_gauge(:pool_capacity, 10)
|
221
|
+
expect_gauge(:threads, 10, "type" => "running")
|
222
|
+
expect_gauge(:threads, 10, "type" => "max")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
84
226
|
|
85
|
-
|
86
|
-
|
227
|
+
context "with single worker stats" do
|
228
|
+
let(:stats_data) do
|
229
|
+
{
|
230
|
+
:backlog => 0,
|
231
|
+
:running => 5,
|
232
|
+
:pool_capacity => 5,
|
233
|
+
:max_threads => 5
|
234
|
+
}
|
235
|
+
end
|
87
236
|
|
88
|
-
|
89
|
-
|
237
|
+
it "calls `puma_gauge` with the (summed) worker metrics" do
|
238
|
+
run_plugin(appsignal_plugin) do
|
239
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
240
|
+
expect_gauge(:connection_backlog, 0)
|
241
|
+
expect_gauge(:pool_capacity, 5)
|
242
|
+
expect_gauge(:threads, 5, "type" => "running")
|
243
|
+
expect_gauge(:threads, 5, "type" => "max")
|
244
|
+
end
|
245
|
+
end
|
90
246
|
end
|
91
247
|
|
92
|
-
|
93
|
-
|
94
|
-
|
248
|
+
context "when using APPSIGNAL_HOSTNAME" do
|
249
|
+
let(:hostname) { "my-host-name" }
|
250
|
+
before { ENV["APPSIGNAL_HOSTNAME"] = hostname }
|
251
|
+
after { ENV.delete("APPSIGNAL_HOSTNAME") }
|
95
252
|
|
96
|
-
|
253
|
+
it "reports the APPSIGNAL_HOSTNAME as the hostname tag value" do
|
254
|
+
run_plugin(appsignal_plugin) do
|
255
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
256
|
+
expect_gauge(:connection_backlog, 1)
|
257
|
+
end
|
258
|
+
end
|
97
259
|
end
|
98
260
|
|
99
|
-
context "without Puma.
|
100
|
-
before
|
261
|
+
context "without Puma.stats_hash" do
|
262
|
+
before do
|
263
|
+
Puma.singleton_class.send(:remove_method, :stats_hash)
|
264
|
+
end
|
101
265
|
|
102
|
-
it "
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
266
|
+
it "fetches metrics from Puma.stats instead" do
|
267
|
+
run_plugin(appsignal_plugin) do
|
268
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
269
|
+
expect(logs).to_not include([kind_of(Symbol), "AppSignal: No Puma stats to report."])
|
270
|
+
expect_gauge(:connection_backlog, 1)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
107
274
|
|
108
|
-
|
109
|
-
|
110
|
-
|
275
|
+
context "without Puma.stats and Puma.stats_hash" do
|
276
|
+
before do
|
277
|
+
Puma.singleton_class.send(:remove_method, :stats)
|
278
|
+
Puma.singleton_class.send(:remove_method, :stats_hash)
|
279
|
+
end
|
111
280
|
|
112
|
-
|
113
|
-
|
281
|
+
it "does not fetch metrics" do
|
282
|
+
run_plugin(appsignal_plugin) do
|
283
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
284
|
+
expect(logs).to include([:log, "AppSignal: No Puma stats to report."])
|
285
|
+
expect(messages).to be_empty
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
114
289
|
|
115
|
-
|
116
|
-
|
290
|
+
context "without running StatsD server" do
|
291
|
+
it "does nothing" do
|
292
|
+
stop_all
|
293
|
+
run_plugin(appsignal_plugin) do
|
294
|
+
expect(logs).to_not include([:error, kind_of(String)])
|
295
|
+
expect(messages).to be_empty
|
296
|
+
end
|
117
297
|
end
|
118
298
|
end
|
119
299
|
end
|
@@ -16,13 +16,25 @@ module WaitForHelper
|
|
16
16
|
def wait_for(name)
|
17
17
|
max_wait = 5_000
|
18
18
|
i = 0
|
19
|
+
error = nil
|
19
20
|
while i < max_wait
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
begin
|
22
|
+
result = yield
|
23
|
+
break if result
|
24
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
25
|
+
# Capture error so we know if it exited with an error
|
26
|
+
error = e
|
27
|
+
ensure
|
28
|
+
i += 1
|
29
|
+
sleep 0.001
|
30
|
+
end
|
23
31
|
end
|
24
32
|
|
25
33
|
return unless i >= max_wait
|
26
|
-
|
34
|
+
error_message =
|
35
|
+
if error
|
36
|
+
"\nError: #{error.class}: #{error.message}\n#{error.backtrace.join("\n")}"
|
37
|
+
end
|
38
|
+
raise "Waited 5 seconds for #{name} condition, but was not met.#{error_message}"
|
27
39
|
end
|
28
40
|
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: 3.0.
|
4
|
+
version: 3.0.10
|
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: 2021-
|
13
|
+
date: 2021-07-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
137
137
|
- ".github/ISSUE_TEMPLATE/chore.md"
|
138
138
|
- ".gitignore"
|
139
|
+
- ".gitmodules"
|
139
140
|
- ".rspec"
|
140
141
|
- ".rubocop.yml"
|
141
142
|
- ".rubocop_todo.yml"
|
@@ -249,7 +250,6 @@ files:
|
|
249
250
|
- lib/appsignal/marker.rb
|
250
251
|
- lib/appsignal/minutely.rb
|
251
252
|
- lib/appsignal/probes.rb
|
252
|
-
- lib/appsignal/probes/puma.rb
|
253
253
|
- lib/appsignal/probes/sidekiq.rb
|
254
254
|
- lib/appsignal/rack/generic_instrumentation.rb
|
255
255
|
- lib/appsignal/rack/rails_instrumentation.rb
|
@@ -271,6 +271,7 @@ files:
|
|
271
271
|
- mono.yml
|
272
272
|
- resources/appsignal.yml.erb
|
273
273
|
- resources/cacert.pem
|
274
|
+
- script/install_lintje
|
274
275
|
- spec/.rubocop.yml
|
275
276
|
- spec/lib/appsignal/auth_check_spec.rb
|
276
277
|
- spec/lib/appsignal/capistrano2_spec.rb
|
@@ -335,7 +336,6 @@ files:
|
|
335
336
|
- spec/lib/appsignal/logger_spec.rb
|
336
337
|
- spec/lib/appsignal/marker_spec.rb
|
337
338
|
- spec/lib/appsignal/minutely_spec.rb
|
338
|
-
- spec/lib/appsignal/probes/puma_spec.rb
|
339
339
|
- spec/lib/appsignal/probes/sidekiq_spec.rb
|
340
340
|
- spec/lib/appsignal/rack/generic_instrumentation_spec.rb
|
341
341
|
- spec/lib/appsignal/rack/rails_instrumentation_spec.rb
|
@@ -484,7 +484,6 @@ test_files:
|
|
484
484
|
- spec/lib/appsignal/logger_spec.rb
|
485
485
|
- spec/lib/appsignal/marker_spec.rb
|
486
486
|
- spec/lib/appsignal/minutely_spec.rb
|
487
|
-
- spec/lib/appsignal/probes/puma_spec.rb
|
488
487
|
- spec/lib/appsignal/probes/sidekiq_spec.rb
|
489
488
|
- spec/lib/appsignal/rack/generic_instrumentation_spec.rb
|
490
489
|
- spec/lib/appsignal/rack/rails_instrumentation_spec.rb
|