appsignal 2.9.15 → 2.9.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/appsignal.gemspec +1 -1
- data/ext/agent.yml +19 -19
- data/lib/appsignal/cli/install.rb +34 -10
- data/lib/appsignal/helpers/instrumentation.rb +8 -4
- data/lib/appsignal/hooks/puma.rb +13 -18
- data/lib/appsignal/transaction.rb +5 -0
- data/lib/appsignal/utils/rails_helper.rb +4 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +26 -0
- data/spec/lib/appsignal/cli/install_spec.rb +51 -7
- data/spec/lib/appsignal/hooks/puma_spec.rb +43 -35
- data/spec/lib/appsignal/minutely_spec.rb +11 -48
- data/spec/lib/appsignal/transaction_spec.rb +27 -4
- data/spec/lib/appsignal_spec.rb +13 -5
- data/spec/lib/puma/appsignal_spec.rb +91 -0
- data/spec/support/helpers/wait_for_helper.rb +28 -0
- data/spec/support/mocks/mock_probe.rb +11 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ea105e6dd4c3e9e9a6f8e5a5df0304bbc438932c820eadd4008548d02beadf9
|
4
|
+
data.tar.gz: 767b716c1860e4a8a6809f5765b8a6b5e97d3fe682f2d8cd8d797c1226b473bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ff2cb1442967bc7e07a50c399d690c1a638a7ceff73e4c093f6157c4b84e3541333f3b169ed69e4574dd2904d07a38f3b737557f0d3c4d800ad7d074671b670
|
7
|
+
data.tar.gz: a06dcd00bb6644394795ce844ea9f1677e68e1b7c3c89ca2c289fc6c313fb5c8cecc6cd56453b3c7666a8c760261f1f3ec08ac74270ba17a20f1d30c690f51a3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.9.17
|
4
|
+
- Handle missing file and load errors from `application.rb` in `appsignal
|
5
|
+
install` for Rails apps. PR #568
|
6
|
+
- Support minutely probes for Puma in clustered mode. PR #570
|
7
|
+
See the installation instructions for the Puma plugin:
|
8
|
+
https://docs.appsignal.com/ruby/integrations/puma.html
|
9
|
+
|
10
|
+
## 2.9.16
|
11
|
+
- Check set_error arguments for Exceptions. PR #565
|
12
|
+
- Bump agent to v-1d8917f - commit 737d6b1b8fc9cd2c0564050bb04246d9267dceb7
|
13
|
+
- Only attempt to send queued payloads if we have a successful transmission.
|
14
|
+
|
3
15
|
## 2.9.15
|
4
16
|
- Bump agent to v-690f4b8 - commit cf4f3787395c8524079f3bed3b2c2367296482a9
|
5
17
|
- Validate transmission_interval option.
|
data/appsignal.gemspec
CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |gem| # rubocop:disable Metrics/BlockLength
|
|
38
38
|
gem.add_dependency "rack"
|
39
39
|
|
40
40
|
gem.add_development_dependency "rake", "~> 11"
|
41
|
-
gem.add_development_dependency "rspec", "~> 3.
|
41
|
+
gem.add_development_dependency "rspec", "~> 3.8"
|
42
42
|
gem.add_development_dependency "pry"
|
43
43
|
gem.add_development_dependency "timecop"
|
44
44
|
gem.add_development_dependency "webmock"
|
data/ext/agent.yml
CHANGED
@@ -1,70 +1,70 @@
|
|
1
1
|
---
|
2
|
-
version:
|
2
|
+
version: 1d8917f
|
3
3
|
mirrors:
|
4
4
|
- https://appsignal-agent-releases.global.ssl.fastly.net
|
5
5
|
- https://d135dj0rjqvssy.cloudfront.net
|
6
6
|
triples:
|
7
7
|
x86_64-darwin:
|
8
8
|
static:
|
9
|
-
checksum:
|
9
|
+
checksum: 777cf5c54eeb32d9d0d6de48c61f14d977244351a4a9e70c9b30c4221e38012c
|
10
10
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
11
11
|
dynamic:
|
12
|
-
checksum:
|
12
|
+
checksum: 2741e747961dbac985f5740c3c747c7a76ce88452a37604ae8c8588343a08464
|
13
13
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
14
14
|
universal-darwin:
|
15
15
|
static:
|
16
|
-
checksum:
|
16
|
+
checksum: 777cf5c54eeb32d9d0d6de48c61f14d977244351a4a9e70c9b30c4221e38012c
|
17
17
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
18
18
|
dynamic:
|
19
|
-
checksum:
|
19
|
+
checksum: 2741e747961dbac985f5740c3c747c7a76ce88452a37604ae8c8588343a08464
|
20
20
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
21
21
|
i686-linux:
|
22
22
|
static:
|
23
|
-
checksum:
|
23
|
+
checksum: 483417f3afbc76b959d68137717b85d44e9dd14c56bad3a44fd370294dc86273
|
24
24
|
filename: appsignal-i686-linux-all-static.tar.gz
|
25
25
|
dynamic:
|
26
|
-
checksum:
|
26
|
+
checksum: b34d007f69eea87a197e5bc8b0cd22f512dc37b732c2ae8c49f053c70c291d2c
|
27
27
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
28
28
|
x86-linux:
|
29
29
|
static:
|
30
|
-
checksum:
|
30
|
+
checksum: 483417f3afbc76b959d68137717b85d44e9dd14c56bad3a44fd370294dc86273
|
31
31
|
filename: appsignal-i686-linux-all-static.tar.gz
|
32
32
|
dynamic:
|
33
|
-
checksum:
|
33
|
+
checksum: b34d007f69eea87a197e5bc8b0cd22f512dc37b732c2ae8c49f053c70c291d2c
|
34
34
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
35
35
|
i686-linux-musl:
|
36
36
|
static:
|
37
|
-
checksum:
|
37
|
+
checksum: ad58fec875496aae76c870e5a1abdf37af27e4d237fc791c4820ef8ccfa05586
|
38
38
|
filename: appsignal-i686-linux-musl-all-static.tar.gz
|
39
39
|
x86-linux-musl:
|
40
40
|
static:
|
41
|
-
checksum:
|
41
|
+
checksum: ad58fec875496aae76c870e5a1abdf37af27e4d237fc791c4820ef8ccfa05586
|
42
42
|
filename: appsignal-i686-linux-musl-all-static.tar.gz
|
43
43
|
x86_64-linux:
|
44
44
|
static:
|
45
|
-
checksum:
|
45
|
+
checksum: dcab7852e81cbacb0a187cb62ce2f583f3ff835fb7c49c09f4e41df64b4b81c5
|
46
46
|
filename: appsignal-x86_64-linux-all-static.tar.gz
|
47
47
|
dynamic:
|
48
|
-
checksum:
|
48
|
+
checksum: 33523662808254ae1e630e33a2466bf17acfb636e3726d98d11b375619ea2cdf
|
49
49
|
filename: appsignal-x86_64-linux-all-dynamic.tar.gz
|
50
50
|
x86_64-linux-musl:
|
51
51
|
static:
|
52
|
-
checksum:
|
52
|
+
checksum: 63cb4ac3d8befaec47eb907b1ff4c6c4af93e39fd7696db783cb6e656dda297c
|
53
53
|
filename: appsignal-x86_64-linux-musl-all-static.tar.gz
|
54
54
|
dynamic:
|
55
|
-
checksum:
|
55
|
+
checksum: 4116c2757c2be2b8eb259d787034ec4ab4123889735ded1ef584afbd68a8247a
|
56
56
|
filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
|
57
57
|
x86_64-freebsd:
|
58
58
|
static:
|
59
|
-
checksum:
|
59
|
+
checksum: 39c006dd131d1ca452ff79ec988688a69823e0abf26ee6966b1639cc42720416
|
60
60
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
61
61
|
dynamic:
|
62
|
-
checksum:
|
62
|
+
checksum: 937fb029f9f6a57aa818d46dcf8fbbfe06481c0765ad591859fbc6970a32a1df
|
63
63
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
64
64
|
amd64-freebsd:
|
65
65
|
static:
|
66
|
-
checksum:
|
66
|
+
checksum: 39c006dd131d1ca452ff79ec988688a69823e0abf26ee6966b1639cc42720416
|
67
67
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
68
68
|
dynamic:
|
69
|
-
checksum:
|
69
|
+
checksum: 937fb029f9f6a57aa818d46dcf8fbbfe06481c0765ad591859fbc6970a32a1df
|
70
70
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
@@ -75,18 +75,39 @@ module Appsignal
|
|
75
75
|
def install_for_rails(config)
|
76
76
|
puts "Installing for Ruby on Rails"
|
77
77
|
|
78
|
-
|
78
|
+
name_overwritten = configure_rails_app_name(config)
|
79
|
+
configure(config, rails_environments, name_overwritten)
|
80
|
+
done_notice
|
81
|
+
end
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
def configure_rails_app_name(config)
|
84
|
+
loaded =
|
85
|
+
begin
|
86
|
+
load Appsignal::Utils::RailsHelper.application_config_path
|
87
|
+
true
|
88
|
+
rescue LoadError, StandardError
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
name_overwritten = false
|
93
|
+
if loaded
|
94
|
+
config[:name] = Appsignal::Utils::RailsHelper.detected_rails_app_name
|
95
|
+
puts
|
96
|
+
name_overwritten = yes_or_no(
|
97
|
+
" Your app's name is: '#{config[:name]}' \n " \
|
98
|
+
"Do you want to change how this is displayed in AppSignal? " \
|
99
|
+
"(y/n): "
|
100
|
+
)
|
101
|
+
if name_overwritten
|
102
|
+
config[:name] = required_input(" Choose app's display name: ")
|
103
|
+
puts
|
104
|
+
end
|
105
|
+
else
|
106
|
+
puts " Unable to automatically detect your Rails app's name."
|
107
|
+
config[:name] = required_input(" Choose your app's display name for AppSignal.com: ")
|
85
108
|
puts
|
86
109
|
end
|
87
|
-
|
88
|
-
configure(config, rails_environments, name_overwritten)
|
89
|
-
done_notice
|
110
|
+
name_overwritten
|
90
111
|
end
|
91
112
|
|
92
113
|
def install_for_sinatra(config)
|
@@ -227,7 +248,10 @@ module Appsignal
|
|
227
248
|
|
228
249
|
def installed_frameworks
|
229
250
|
[].tap do |out|
|
230
|
-
|
251
|
+
if framework_available?("rails") &&
|
252
|
+
File.exist?(Appsignal::Utils::RailsHelper.application_config_path)
|
253
|
+
out << :rails
|
254
|
+
end
|
231
255
|
out << :sinatra if framework_available? "sinatra"
|
232
256
|
out << :padrino if framework_available? "padrino"
|
233
257
|
out << :grape if framework_available? "grape"
|
@@ -201,7 +201,8 @@ module Appsignal
|
|
201
201
|
)
|
202
202
|
return unless active?
|
203
203
|
unless error.is_a?(Exception)
|
204
|
-
logger.error
|
204
|
+
logger.error "Appsignal.send_error: Cannot send error. The given " \
|
205
|
+
"value is not an exception: #{error.inspect}"
|
205
206
|
return
|
206
207
|
end
|
207
208
|
transaction = Appsignal::Transaction.new(
|
@@ -256,9 +257,12 @@ module Appsignal
|
|
256
257
|
# Exception handling guide
|
257
258
|
# @since 0.6.6
|
258
259
|
def set_error(exception, tags = nil, namespace = nil)
|
259
|
-
|
260
|
-
|
261
|
-
exception.
|
260
|
+
unless exception.is_a?(Exception)
|
261
|
+
logger.error "Appsignal.set_error: Cannot set error. The given " \
|
262
|
+
"value is not an exception: #{exception.inspect}"
|
263
|
+
return
|
264
|
+
end
|
265
|
+
return if !active? || Appsignal::Transaction.current.nil?
|
262
266
|
transaction = Appsignal::Transaction.current
|
263
267
|
transaction.set_error(exception)
|
264
268
|
transaction.set_tags(tags) if tags
|
data/lib/appsignal/hooks/puma.rb
CHANGED
@@ -11,27 +11,22 @@ module Appsignal
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def install
|
14
|
-
if ::Puma.respond_to?(:stats)
|
14
|
+
if ::Puma.respond_to?(:stats) && !defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)
|
15
|
+
# Only install the minutely probe if a user isn't using our Puma
|
16
|
+
# plugin, which lives in `lib/puma/appsignal.rb`. This plugin defines
|
17
|
+
# the {APPSIGNAL_PUMA_PLUGIN_LOADED} constant.
|
18
|
+
#
|
19
|
+
# We prefer people use the AppSignal Puma plugin. This fallback is
|
20
|
+
# only there when users relied on our *magic* integration.
|
21
|
+
#
|
22
|
+
# Using the Puma plugin, the minutely probe thread will still run in
|
23
|
+
# Puma workers, for other non-Puma probes, but the Puma probe only
|
24
|
+
# runs in the Puma main process.
|
25
|
+
# For more information:
|
26
|
+
# https://docs.appsignal.com/ruby/integrations/puma.html
|
15
27
|
Appsignal::Minutely.probes.register :puma, PumaProbe
|
16
28
|
end
|
17
29
|
|
18
|
-
if ::Puma.respond_to?(:cli_config) && ::Puma.cli_config
|
19
|
-
::Puma.cli_config.options[:before_fork] ||= []
|
20
|
-
::Puma.cli_config.options[:before_fork] << proc do |_id|
|
21
|
-
Appsignal::Minutely.start
|
22
|
-
end
|
23
|
-
|
24
|
-
::Puma.cli_config.options[:before_worker_boot] ||= []
|
25
|
-
::Puma.cli_config.options[:before_worker_boot] << proc do |_id|
|
26
|
-
Appsignal.forked
|
27
|
-
end
|
28
|
-
|
29
|
-
::Puma.cli_config.options[:before_worker_shutdown] ||= []
|
30
|
-
::Puma.cli_config.options[:before_worker_shutdown] << proc do |_id|
|
31
|
-
Appsignal.stop("puma before_worker_shutdown")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
30
|
return unless defined?(::Puma::Cluster)
|
36
31
|
# For clustered mode with multiple workers
|
37
32
|
::Puma::Cluster.class_eval do
|
@@ -264,6 +264,11 @@ module Appsignal
|
|
264
264
|
end
|
265
265
|
|
266
266
|
def set_error(error)
|
267
|
+
unless error.is_a?(Exception)
|
268
|
+
Appsignal.logger.error "Appsignal::Transaction#set_error: Cannot set error. " \
|
269
|
+
"The given value is not an exception: #{error.inspect}"
|
270
|
+
return
|
271
|
+
end
|
267
272
|
return unless error
|
268
273
|
return unless Appsignal.active?
|
269
274
|
|
data/lib/appsignal/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
APPSIGNAL_PUMA_PLUGIN_LOADED = true
|
2
|
+
|
3
|
+
# AppSignal Puma plugin
|
4
|
+
#
|
5
|
+
# This plugin ensures the minutely probe thread is started with the Puma
|
6
|
+
# minutely probe in the Puma master process.
|
7
|
+
#
|
8
|
+
# The constant {APPSIGNAL_PUMA_PLUGIN_LOADED} is here to mark the Plugin as
|
9
|
+
# loaded by the rest of the AppSignal gem. This ensures that the Puma minutely
|
10
|
+
# probe is not also started in every Puma workers, which was the old behavior.
|
11
|
+
# See {Appsignal::Hooks::PumaHook#install} for more information.
|
12
|
+
#
|
13
|
+
# For even more information:
|
14
|
+
# https://docs.appsignal.com/ruby/integrations/puma.html
|
15
|
+
Puma::Plugin.create do
|
16
|
+
def start(launcher = nil)
|
17
|
+
launcher.events.on_booted do
|
18
|
+
require "appsignal"
|
19
|
+
if ::Puma.respond_to?(:stats)
|
20
|
+
Appsignal::Minutely.probes.register :puma, Appsignal::Hooks::PumaProbe
|
21
|
+
end
|
22
|
+
Appsignal.start
|
23
|
+
Appsignal.start_logger
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -16,6 +16,10 @@ describe Appsignal::CLI::Install do
|
|
16
16
|
allow(described_class).to receive(:press_any_key)
|
17
17
|
allow(Appsignal::Demo).to receive(:transmit).and_return(true)
|
18
18
|
end
|
19
|
+
after do
|
20
|
+
FileUtils.rm_rf(tmp_dir)
|
21
|
+
FileUtils.mkdir_p(tmp_dir)
|
22
|
+
end
|
19
23
|
around do |example|
|
20
24
|
original_stdin = $stdin
|
21
25
|
$stdin = StringIO.new
|
@@ -157,16 +161,10 @@ describe Appsignal::CLI::Install do
|
|
157
161
|
shared_examples "capistrano install" do
|
158
162
|
let(:capfile) { File.join(tmp_dir, "Capfile") }
|
159
163
|
before do
|
160
|
-
FileUtils.mkdir_p(tmp_dir)
|
161
|
-
|
162
164
|
enter_app_name "foo"
|
163
165
|
add_cli_input "n"
|
164
166
|
choose_environment_config
|
165
167
|
end
|
166
|
-
after do
|
167
|
-
FileUtils.rm_rf(tmp_dir)
|
168
|
-
FileUtils.mkdir_p(tmp_dir)
|
169
|
-
end
|
170
168
|
|
171
169
|
context "without Capfile" do
|
172
170
|
it "does nothing" do
|
@@ -260,7 +258,6 @@ describe Appsignal::CLI::Install do
|
|
260
258
|
FileUtils.touch(File.join(environments_dir, "development.rb"))
|
261
259
|
FileUtils.touch(File.join(environments_dir, "staging.rb"))
|
262
260
|
FileUtils.touch(File.join(environments_dir, "production.rb"))
|
263
|
-
enter_app_name app_name
|
264
261
|
end
|
265
262
|
|
266
263
|
describe "environments" do
|
@@ -410,6 +407,53 @@ describe Appsignal::CLI::Install do
|
|
410
407
|
end
|
411
408
|
end
|
412
409
|
end
|
410
|
+
|
411
|
+
context "when there is no Rails application.rb file" do
|
412
|
+
before do
|
413
|
+
# Do not detect it as another framework for testing
|
414
|
+
allow(described_class).to receive(:framework_available?).and_call_original
|
415
|
+
allow(described_class).to receive(:framework_available?).with("sinatra").and_return(false)
|
416
|
+
|
417
|
+
File.delete(File.join(config_dir, "application.rb"))
|
418
|
+
expect(File.exist?(File.join(config_dir, "application.rb"))).to eql(false)
|
419
|
+
end
|
420
|
+
|
421
|
+
it "fails the installation" do
|
422
|
+
run
|
423
|
+
|
424
|
+
expect(output).to include("We could not detect which framework you are using.")
|
425
|
+
expect(output).to_not include("Installing for Ruby on Rails")
|
426
|
+
expect(output).to include_complete_install
|
427
|
+
|
428
|
+
expect(File.exist?(config_file_path)).to be(false)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context "when failed to load the Rails application.rb file" do
|
433
|
+
before do
|
434
|
+
File.open(File.join(config_dir, "application.rb"), "w") do |file|
|
435
|
+
file.write("I am invalid code")
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
it "prompts the user to fill in an app name" do
|
440
|
+
enter_app_name app_name
|
441
|
+
choose_config_file
|
442
|
+
run
|
443
|
+
|
444
|
+
expect(output).to include("Installing for Ruby on Rails")
|
445
|
+
expect(output).to include("Unable to automatically detect your Rails app's name.")
|
446
|
+
expect(output).to include("Choose your app's display name for AppSignal.com:")
|
447
|
+
expect(output).to include_file_config
|
448
|
+
expect(output).to include_complete_install
|
449
|
+
|
450
|
+
expect(config_file).to configure_app_name(app_name)
|
451
|
+
expect(config_file).to configure_push_api_key(push_api_key)
|
452
|
+
expect(config_file).to configure_environment("development")
|
453
|
+
expect(config_file).to configure_environment("staging")
|
454
|
+
expect(config_file).to configure_environment("production")
|
455
|
+
end
|
456
|
+
end
|
413
457
|
end
|
414
458
|
end
|
415
459
|
|
@@ -27,6 +27,8 @@ describe Appsignal::Hooks::PumaHook do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
describe "installation" do
|
30
|
+
before { Appsignal::Minutely.probes.clear }
|
31
|
+
|
30
32
|
context "when not clustered mode" do
|
31
33
|
it "does not add AppSignal stop behavior Puma::Cluster" do
|
32
34
|
expect(defined?(::Puma::Cluster)).to be_falsy
|
@@ -34,9 +36,27 @@ describe Appsignal::Hooks::PumaHook do
|
|
34
36
|
Appsignal::Hooks::PumaHook.new.install
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
40
|
+
before do
|
41
|
+
# Set in lib/puma/appsignal.rb
|
42
|
+
APPSIGNAL_PUMA_PLUGIN_LOADED = true
|
43
|
+
end
|
44
|
+
after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
|
45
|
+
|
46
|
+
it "does not add the Puma minutely probe" do
|
47
|
+
Appsignal::Hooks::PumaHook.new.install
|
48
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
53
|
+
it "adds the Puma minutely probe" do
|
54
|
+
expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
|
55
|
+
|
56
|
+
Appsignal::Hooks::PumaHook.new.install
|
57
|
+
probe = Appsignal::Minutely.probes[:puma]
|
58
|
+
expect(probe).to eql(Appsignal::Hooks::PumaProbe)
|
59
|
+
end
|
40
60
|
end
|
41
61
|
end
|
42
62
|
|
@@ -49,11 +69,11 @@ describe Appsignal::Hooks::PumaHook do
|
|
49
69
|
end
|
50
70
|
end
|
51
71
|
end
|
52
|
-
Appsignal::Hooks::PumaHook.new.install
|
53
72
|
end
|
54
73
|
after { Puma.send(:remove_const, :Cluster) }
|
55
74
|
|
56
75
|
it "adds behavior to Puma::Cluster.stop_workers" do
|
76
|
+
Appsignal::Hooks::PumaHook.new.install
|
57
77
|
cluster = Puma::Cluster.new
|
58
78
|
|
59
79
|
expect(cluster.instance_variable_defined?(:@called)).to be_falsy
|
@@ -62,40 +82,28 @@ describe Appsignal::Hooks::PumaHook do
|
|
62
82
|
expect(cluster.instance_variable_get(:@called)).to be(true)
|
63
83
|
end
|
64
84
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
context "with nil hooks" do
|
73
|
-
before do
|
74
|
-
Puma.cli_config.options.delete(:before_fork)
|
75
|
-
Puma.cli_config.options.delete(:before_worker_boot)
|
76
|
-
Puma.cli_config.options.delete(:before_worker_shutdown)
|
77
|
-
Appsignal::Hooks::PumaHook.new.install
|
78
|
-
end
|
85
|
+
context "with APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
86
|
+
before do
|
87
|
+
# Set in lib/puma/appsignal.rb
|
88
|
+
APPSIGNAL_PUMA_PLUGIN_LOADED = true
|
89
|
+
end
|
90
|
+
after { Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED }
|
79
91
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
92
|
+
it "does not add the Puma minutely probe" do
|
93
|
+
Appsignal::Hooks::PumaHook.new.install
|
94
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
95
|
+
end
|
96
|
+
end
|
86
97
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
Puma.cli_config.options[:before_worker_boot] = []
|
91
|
-
Puma.cli_config.options[:before_worker_shutdown] = []
|
92
|
-
Appsignal::Hooks::PumaHook.new.install
|
93
|
-
end
|
98
|
+
context "without APPSIGNAL_PUMA_PLUGIN_LOADED defined" do
|
99
|
+
it "adds the Puma minutely probe" do
|
100
|
+
expect(defined?(APPSIGNAL_PUMA_PLUGIN_LOADED)).to be_nil
|
94
101
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
102
|
+
Appsignal::Hooks::PumaHook.new.install
|
103
|
+
probe = Appsignal::Minutely.probes[:puma]
|
104
|
+
expect(probe).to eql(Appsignal::Hooks::PumaProbe)
|
105
|
+
end
|
106
|
+
end
|
99
107
|
end
|
100
108
|
end
|
101
109
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
describe Appsignal::Minutely do
|
2
|
+
include WaitForHelper
|
3
|
+
|
2
4
|
before { Appsignal::Minutely.probes.clear }
|
3
5
|
|
4
6
|
it "returns a ProbeCollection" do
|
@@ -7,38 +9,26 @@ describe Appsignal::Minutely do
|
|
7
9
|
end
|
8
10
|
|
9
11
|
describe ".start" do
|
10
|
-
class
|
11
|
-
attr_reader :calls
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@calls = 0
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
@calls += 1
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class ProbeWithoutDependency < Probe
|
12
|
+
class ProbeWithoutDependency < MockProbe
|
23
13
|
def self.dependencies_present?
|
24
14
|
true
|
25
15
|
end
|
26
16
|
end
|
27
17
|
|
28
|
-
class ProbeWithMissingDependency <
|
18
|
+
class ProbeWithMissingDependency < MockProbe
|
29
19
|
def self.dependencies_present?
|
30
20
|
false
|
31
21
|
end
|
32
22
|
end
|
33
23
|
|
34
|
-
class BrokenProbe <
|
24
|
+
class BrokenProbe < MockProbe
|
35
25
|
def call
|
36
26
|
super
|
37
27
|
raise "oh no!"
|
38
28
|
end
|
39
29
|
end
|
40
30
|
|
41
|
-
class BrokenProbeOnInitialize <
|
31
|
+
class BrokenProbeOnInitialize < MockProbe
|
42
32
|
def initialize
|
43
33
|
super
|
44
34
|
raise "oh no initialize!"
|
@@ -60,7 +50,7 @@ describe Appsignal::Minutely do
|
|
60
50
|
|
61
51
|
context "with an instance of a class" do
|
62
52
|
it "calls the probe every <wait_time>" do
|
63
|
-
probe =
|
53
|
+
probe = MockProbe.new
|
64
54
|
Appsignal::Minutely.probes.register :my_probe, probe
|
65
55
|
Appsignal::Minutely.start
|
66
56
|
|
@@ -90,8 +80,8 @@ describe Appsignal::Minutely do
|
|
90
80
|
|
91
81
|
context "with probe class" do
|
92
82
|
it "creates an instance of the class and call that every <wait time>" do
|
93
|
-
probe =
|
94
|
-
probe_instance =
|
83
|
+
probe = MockProbe
|
84
|
+
probe_instance = MockProbe.new
|
95
85
|
expect(probe).to receive(:new).and_return(probe_instance)
|
96
86
|
Appsignal::Minutely.probes.register :my_probe, probe
|
97
87
|
Appsignal::Minutely.start
|
@@ -163,7 +153,7 @@ describe Appsignal::Minutely do
|
|
163
153
|
|
164
154
|
context "with a broken probe" do
|
165
155
|
it "logs the error and continues calling the probes every <wait_time>" do
|
166
|
-
probe =
|
156
|
+
probe = MockProbe.new
|
167
157
|
broken_probe = BrokenProbe.new
|
168
158
|
Appsignal::Minutely.probes.register :my_probe, probe
|
169
159
|
Appsignal::Minutely.probes.register :broken_probe, broken_probe
|
@@ -183,7 +173,7 @@ describe Appsignal::Minutely do
|
|
183
173
|
|
184
174
|
it "ensures only one minutely probes thread is active at a time" do
|
185
175
|
alive_thread_counter = proc { Thread.list.reject { |t| t.status == "dead" }.length }
|
186
|
-
probe =
|
176
|
+
probe = MockProbe.new
|
187
177
|
Appsignal::Minutely.probes.register :my_probe, probe
|
188
178
|
expect do
|
189
179
|
Appsignal::Minutely.start
|
@@ -349,31 +339,4 @@ describe Appsignal::Minutely do
|
|
349
339
|
end
|
350
340
|
end
|
351
341
|
end
|
352
|
-
|
353
|
-
# Wait for a condition to be met
|
354
|
-
#
|
355
|
-
# @example
|
356
|
-
# # Perform threaded operation
|
357
|
-
# wait_for("enough probe calls") { probe.calls >= 2 }
|
358
|
-
# # Assert on result
|
359
|
-
#
|
360
|
-
# @param name [String] The name of the condition to check. Used in the
|
361
|
-
# error when it fails.
|
362
|
-
# @yield Assertion to check.
|
363
|
-
# @yieldreturn [Boolean] True/False value that indicates if the condition
|
364
|
-
# is met.
|
365
|
-
# @raise [StandardError] Raises error if the condition is not met after 5
|
366
|
-
# seconds, 5_000 tries.
|
367
|
-
def wait_for(name)
|
368
|
-
max_wait = 5_000
|
369
|
-
i = 0
|
370
|
-
while i <= max_wait
|
371
|
-
break if yield
|
372
|
-
i += 1
|
373
|
-
sleep 0.001
|
374
|
-
end
|
375
|
-
|
376
|
-
return unless i == max_wait
|
377
|
-
raise "Waited 5 seconds for #{name} condition, but was not met."
|
378
|
-
end
|
379
342
|
end
|
@@ -630,7 +630,11 @@ describe Appsignal::Transaction do
|
|
630
630
|
|
631
631
|
describe "#set_error" do
|
632
632
|
let(:env) { http_request_env_with_data }
|
633
|
-
let(:error)
|
633
|
+
let(:error) do
|
634
|
+
e = ExampleStandardError.new("test message")
|
635
|
+
allow(e).to receive(:backtrace).and_return(["line 1"])
|
636
|
+
e
|
637
|
+
end
|
634
638
|
|
635
639
|
it "should also respond to add_exception for backwords compatibility" do
|
636
640
|
expect(transaction).to respond_to(:add_exception)
|
@@ -643,10 +647,24 @@ describe Appsignal::Transaction do
|
|
643
647
|
transaction.set_error(error)
|
644
648
|
end
|
645
649
|
|
650
|
+
context "when error is not an error" do
|
651
|
+
let(:error) { Object.new }
|
652
|
+
|
653
|
+
it "does not add the error" do
|
654
|
+
expect(Appsignal.logger).to receive(:error).with(
|
655
|
+
"Appsignal::Transaction#set_error: Cannot set error. " \
|
656
|
+
"The given value is not an exception: #{error.inspect}"
|
657
|
+
)
|
658
|
+
expect(transaction.ext).to_not receive(:set_error)
|
659
|
+
|
660
|
+
transaction.set_error(error)
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
646
664
|
context "for a http request" do
|
647
665
|
it "should set an error in the extension" do
|
648
666
|
expect(transaction.ext).to receive(:set_error).with(
|
649
|
-
"
|
667
|
+
"ExampleStandardError",
|
650
668
|
"test message",
|
651
669
|
Appsignal::Utils::Data.generate(["line 1"])
|
652
670
|
)
|
@@ -656,7 +674,12 @@ describe Appsignal::Transaction do
|
|
656
674
|
end
|
657
675
|
|
658
676
|
context "when error message is nil" do
|
659
|
-
let(:error)
|
677
|
+
let(:error) do
|
678
|
+
e = ExampleStandardError.new
|
679
|
+
allow(e).to receive(:message).and_return(nil)
|
680
|
+
allow(e).to receive(:backtrace).and_return(["line 1"])
|
681
|
+
e
|
682
|
+
end
|
660
683
|
|
661
684
|
it "should not raise an error" do
|
662
685
|
expect { transaction.set_error(error) }.to_not raise_error
|
@@ -664,7 +687,7 @@ describe Appsignal::Transaction do
|
|
664
687
|
|
665
688
|
it "should set an error in the extension" do
|
666
689
|
expect(transaction.ext).to receive(:set_error).with(
|
667
|
-
"
|
690
|
+
"ExampleStandardError",
|
668
691
|
"",
|
669
692
|
Appsignal::Utils::Data.generate(["line 1"])
|
670
693
|
)
|
data/spec/lib/appsignal_spec.rb
CHANGED
@@ -657,8 +657,10 @@ describe Appsignal do
|
|
657
657
|
let(:error) { double }
|
658
658
|
|
659
659
|
it "logs an error message" do
|
660
|
-
expect(Appsignal.logger).to receive(:error)
|
661
|
-
.
|
660
|
+
expect(Appsignal.logger).to receive(:error).with(
|
661
|
+
"Appsignal.send_error: Cannot send error. " \
|
662
|
+
"The given value is not an exception: #{error.inspect}"
|
663
|
+
)
|
662
664
|
end
|
663
665
|
|
664
666
|
it "does not send the error" do
|
@@ -778,13 +780,19 @@ describe Appsignal do
|
|
778
780
|
Appsignal.set_error(error)
|
779
781
|
end
|
780
782
|
|
781
|
-
context "when the error is
|
782
|
-
|
783
|
+
context "when the error is not an Exception" do
|
784
|
+
let(:error) { Object.new }
|
785
|
+
|
786
|
+
it "logs an error" do
|
787
|
+
expect(Appsignal.logger).to receive(:error).with(
|
788
|
+
"Appsignal.set_error: Cannot set error. " \
|
789
|
+
"The given value is not an exception: #{error.inspect}"
|
790
|
+
)
|
783
791
|
expect(transaction).to_not receive(:set_error)
|
784
792
|
expect(transaction).to_not receive(:set_tags)
|
785
793
|
expect(transaction).to_not receive(:set_namespace)
|
786
794
|
|
787
|
-
Appsignal.set_error(
|
795
|
+
Appsignal.set_error(error)
|
788
796
|
end
|
789
797
|
end
|
790
798
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
RSpec.describe "Puma plugin" do
|
2
|
+
include WaitForHelper
|
3
|
+
|
4
|
+
class MockPumaLauncher
|
5
|
+
def events
|
6
|
+
return @events if defined?(@events)
|
7
|
+
|
8
|
+
@events = MockPumaEvents.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class MockPumaEvents
|
13
|
+
def on_booted(&block)
|
14
|
+
@on_booted = block if block_given?
|
15
|
+
@on_booted if defined?(@on_booted)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:probe) { MockProbe.new }
|
20
|
+
let(:launcher) { MockPumaLauncher.new }
|
21
|
+
before do
|
22
|
+
module Puma
|
23
|
+
def self.stats
|
24
|
+
end
|
25
|
+
|
26
|
+
class Plugin
|
27
|
+
class << self
|
28
|
+
attr_reader :plugin
|
29
|
+
|
30
|
+
def create(&block)
|
31
|
+
@plugin = Class.new(::Puma::Plugin)
|
32
|
+
@plugin.class_eval(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Appsignal::Minutely.probes.clear
|
39
|
+
ENV["APPSIGNAL_ENABLE_MINUTELY_PROBES"] = "true"
|
40
|
+
Appsignal.config = project_fixture_config
|
41
|
+
# Speed up test time
|
42
|
+
allow(Appsignal::Minutely).to receive(:initial_wait_time).and_return(0.001)
|
43
|
+
allow(Appsignal::Minutely).to receive(:wait_time).and_return(0.001)
|
44
|
+
|
45
|
+
Appsignal::Minutely.probes.register :my_probe, probe
|
46
|
+
load File.expand_path("../lib/puma/plugin/appsignal.rb", APPSIGNAL_SPEC_DIR)
|
47
|
+
end
|
48
|
+
after do
|
49
|
+
Appsignal.config = nil
|
50
|
+
Object.send :remove_const, :Puma
|
51
|
+
Object.send :remove_const, :APPSIGNAL_PUMA_PLUGIN_LOADED
|
52
|
+
end
|
53
|
+
|
54
|
+
it "registers the PumaProbe" do
|
55
|
+
expect(Appsignal::Minutely.probes[:my_probe]).to eql(probe)
|
56
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
57
|
+
plugin = Puma::Plugin.plugin.new
|
58
|
+
expect(launcher.events.on_booted).to be_nil
|
59
|
+
|
60
|
+
plugin.start(launcher)
|
61
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
62
|
+
expect(launcher.events.on_booted).to_not be_nil
|
63
|
+
|
64
|
+
launcher.events.on_booted.call
|
65
|
+
expect(Appsignal::Minutely.probes[:puma]).to eql(Appsignal::Hooks::PumaProbe)
|
66
|
+
|
67
|
+
# Minutely probes started and called
|
68
|
+
wait_for("enough probe calls") { probe.calls >= 2 }
|
69
|
+
end
|
70
|
+
|
71
|
+
context "without Puma.stats" do
|
72
|
+
before { Puma.singleton_class.send(:remove_method, :stats) }
|
73
|
+
|
74
|
+
it "does not register the PumaProbe" do
|
75
|
+
expect(Appsignal::Minutely.probes[:my_probe]).to eql(probe)
|
76
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
77
|
+
plugin = Puma::Plugin.plugin.new
|
78
|
+
expect(launcher.events.on_booted).to be_nil
|
79
|
+
|
80
|
+
plugin.start(launcher)
|
81
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
82
|
+
expect(launcher.events.on_booted).to_not be_nil
|
83
|
+
|
84
|
+
launcher.events.on_booted.call
|
85
|
+
expect(Appsignal::Minutely.probes[:puma]).to be_nil
|
86
|
+
|
87
|
+
# Minutely probes started and called
|
88
|
+
wait_for("enough probe calls") { probe.calls >= 2 }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WaitForHelper
|
2
|
+
# Wait for a condition to be met
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# # Perform threaded operation
|
6
|
+
# wait_for("enough probe calls") { probe.calls >= 2 }
|
7
|
+
# # Assert on result
|
8
|
+
#
|
9
|
+
# @param name [String] The name of the condition to check. Used in the
|
10
|
+
# error when it fails.
|
11
|
+
# @yield Assertion to check.
|
12
|
+
# @yieldreturn [Boolean] True/False value that indicates if the condition
|
13
|
+
# is met.
|
14
|
+
# @raise [StandardError] Raises error if the condition is not met after 5
|
15
|
+
# seconds, 5_000 tries.
|
16
|
+
def wait_for(name)
|
17
|
+
max_wait = 5_000
|
18
|
+
i = 0
|
19
|
+
while i <= max_wait
|
20
|
+
break if yield
|
21
|
+
i += 1
|
22
|
+
sleep 0.001
|
23
|
+
end
|
24
|
+
|
25
|
+
return unless i == max_wait
|
26
|
+
raise "Waited 5 seconds for #{name} condition, but was not met."
|
27
|
+
end
|
28
|
+
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.9.
|
4
|
+
version: 2.9.17
|
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: 2019-
|
13
|
+
date: 2019-11-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -46,14 +46,14 @@ dependencies:
|
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '3.
|
49
|
+
version: '3.8'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
54
|
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: '3.
|
56
|
+
version: '3.8'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: pry
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
@@ -255,6 +255,7 @@ files:
|
|
255
255
|
- lib/appsignal/utils/query_params_sanitizer.rb
|
256
256
|
- lib/appsignal/utils/rails_helper.rb
|
257
257
|
- lib/appsignal/version.rb
|
258
|
+
- lib/puma/plugin/appsignal.rb
|
258
259
|
- lib/sequel/extensions/appsignal_integration.rb
|
259
260
|
- resources/appsignal.yml.erb
|
260
261
|
- resources/cacert.pem
|
@@ -328,6 +329,7 @@ files:
|
|
328
329
|
- spec/lib/appsignal/utils/json_spec.rb
|
329
330
|
- spec/lib/appsignal/utils/query_params_sanitizer_spec.rb
|
330
331
|
- spec/lib/appsignal_spec.rb
|
332
|
+
- spec/lib/puma/appsignal_spec.rb
|
331
333
|
- spec/spec_helper.rb
|
332
334
|
- spec/support/fixtures/generated_config.yml
|
333
335
|
- spec/support/fixtures/projects/valid/config/application.rb
|
@@ -350,9 +352,11 @@ files:
|
|
350
352
|
- spec/support/helpers/system_helpers.rb
|
351
353
|
- spec/support/helpers/time_helpers.rb
|
352
354
|
- spec/support/helpers/transaction_helpers.rb
|
355
|
+
- spec/support/helpers/wait_for_helper.rb
|
353
356
|
- spec/support/matchers/contains_log.rb
|
354
357
|
- spec/support/mocks/fake_gc_profiler.rb
|
355
358
|
- spec/support/mocks/mock_extension.rb
|
359
|
+
- spec/support/mocks/mock_probe.rb
|
356
360
|
- spec/support/rails/my_app.rb
|
357
361
|
- spec/support/shared_examples/instrument.rb
|
358
362
|
- spec/support/stubs/delayed_job.rb
|
@@ -384,7 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
384
388
|
- !ruby/object:Gem::Version
|
385
389
|
version: '0'
|
386
390
|
requirements: []
|
387
|
-
rubygems_version: 3.0.
|
391
|
+
rubygems_version: 3.0.6
|
388
392
|
signing_key:
|
389
393
|
specification_version: 4
|
390
394
|
summary: Logs performance and exception data from your app to appsignal.com
|
@@ -459,6 +463,7 @@ test_files:
|
|
459
463
|
- spec/lib/appsignal/utils/json_spec.rb
|
460
464
|
- spec/lib/appsignal/utils/query_params_sanitizer_spec.rb
|
461
465
|
- spec/lib/appsignal_spec.rb
|
466
|
+
- spec/lib/puma/appsignal_spec.rb
|
462
467
|
- spec/spec_helper.rb
|
463
468
|
- spec/support/fixtures/generated_config.yml
|
464
469
|
- spec/support/fixtures/projects/valid/config/application.rb
|
@@ -481,9 +486,11 @@ test_files:
|
|
481
486
|
- spec/support/helpers/system_helpers.rb
|
482
487
|
- spec/support/helpers/time_helpers.rb
|
483
488
|
- spec/support/helpers/transaction_helpers.rb
|
489
|
+
- spec/support/helpers/wait_for_helper.rb
|
484
490
|
- spec/support/matchers/contains_log.rb
|
485
491
|
- spec/support/mocks/fake_gc_profiler.rb
|
486
492
|
- spec/support/mocks/mock_extension.rb
|
493
|
+
- spec/support/mocks/mock_probe.rb
|
487
494
|
- spec/support/rails/my_app.rb
|
488
495
|
- spec/support/shared_examples/instrument.rb
|
489
496
|
- spec/support/stubs/delayed_job.rb
|