appsignal 2.9.16 → 2.9.18

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05276a7162dde5c58464ac4d6170837a4a71522f42024b77fb456f35773c74b3
4
- data.tar.gz: 8005bfc2837f546de89a5703ab0fdbd5a340f53f86871dc64de6550c3f728c26
3
+ metadata.gz: 3d3694d0f2970749ad1d8951a3989c2f6ea77c10f3cb695c3992266962bba09e
4
+ data.tar.gz: b16b94135fc7f014ec2a3b5b6904696d6b262972bb229d908f3d0c7f2f6bb6ff
5
5
  SHA512:
6
- metadata.gz: 8906bffe72c3e8cceb40f1eb6e32827c4389acb19706fcd992901dc00074779ad2121e3d1c7f45ba9f876929c4bb2cb357808c5bd81d01bf3c6dd40695441b31
7
- data.tar.gz: d5582d45e830d06d291296136d8802bad1121227af7b6f82192112f991c48621b3fe55836343ff0d592d9770c7043fc4abce04e5626d0115ea491b94e9ab201c
6
+ metadata.gz: cb9e78c6e46f317ea3092e6045afd21abb7bea8ed3858021fbd4d2ef0d4d743a778becf69a3e2c4dc0dd64a00a353b2793601d22ed5163d99c9dfd779f748b8b
7
+ data.tar.gz: fc32dfdfb9beadac21752e730340306674948458a2cc260d66e5dca646efa4bfe7a6c44e01d62a17844f229a08562d55534651c7744d82a9da8c98a8e23e9869
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.9.18 (Beta)
4
+ - Bump agent to v-c348132
5
+ - Improve transmitter logging on timeout
6
+ - Improve queued payloads transmitter. Should prevent payloads being sent
7
+ multiple times.
8
+ - Add transaction debug mode
9
+ - Wrap Option in Mutex in TransactionInProgess
10
+
11
+ ## 2.9.17
12
+ - Handle missing file and load errors from `application.rb` in `appsignal
13
+ install` for Rails apps. PR #568
14
+ - Support minutely probes for Puma in clustered mode. PR #570
15
+ See the installation instructions for the Puma plugin:
16
+ https://docs.appsignal.com/ruby/integrations/puma.html
17
+
3
18
  ## 2.9.16
4
19
  - Check set_error arguments for Exceptions. PR #565
5
20
  - Bump agent to v-1d8917f - commit 737d6b1b8fc9cd2c0564050bb04246d9267dceb7
@@ -1,70 +1,70 @@
1
1
  ---
2
- version: 1d8917f
2
+ version: c348132
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: 777cf5c54eeb32d9d0d6de48c61f14d977244351a4a9e70c9b30c4221e38012c
9
+ checksum: cb287c8e2072fe5b8cf14449bd6892989c392d0c651ce339895ae0302cb69785
10
10
  filename: appsignal-x86_64-darwin-all-static.tar.gz
11
11
  dynamic:
12
- checksum: 2741e747961dbac985f5740c3c747c7a76ce88452a37604ae8c8588343a08464
12
+ checksum: af1ed2e9d29859ffbfc8e6903e4c51764dee94d7b4877ca8d30270b6f133a10f
13
13
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
14
14
  universal-darwin:
15
15
  static:
16
- checksum: 777cf5c54eeb32d9d0d6de48c61f14d977244351a4a9e70c9b30c4221e38012c
16
+ checksum: cb287c8e2072fe5b8cf14449bd6892989c392d0c651ce339895ae0302cb69785
17
17
  filename: appsignal-x86_64-darwin-all-static.tar.gz
18
18
  dynamic:
19
- checksum: 2741e747961dbac985f5740c3c747c7a76ce88452a37604ae8c8588343a08464
19
+ checksum: af1ed2e9d29859ffbfc8e6903e4c51764dee94d7b4877ca8d30270b6f133a10f
20
20
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
21
21
  i686-linux:
22
22
  static:
23
- checksum: 483417f3afbc76b959d68137717b85d44e9dd14c56bad3a44fd370294dc86273
23
+ checksum: 2c3bcd102592bf38fbdb27e7c70502dccbe54a0dc2739a9d54aaa694fcfb41fb
24
24
  filename: appsignal-i686-linux-all-static.tar.gz
25
25
  dynamic:
26
- checksum: b34d007f69eea87a197e5bc8b0cd22f512dc37b732c2ae8c49f053c70c291d2c
26
+ checksum: 1c037b8370b755d706340e25d3e4b2f4acb279dd03873cc53bcf0a6ec0832653
27
27
  filename: appsignal-i686-linux-all-dynamic.tar.gz
28
28
  x86-linux:
29
29
  static:
30
- checksum: 483417f3afbc76b959d68137717b85d44e9dd14c56bad3a44fd370294dc86273
30
+ checksum: 2c3bcd102592bf38fbdb27e7c70502dccbe54a0dc2739a9d54aaa694fcfb41fb
31
31
  filename: appsignal-i686-linux-all-static.tar.gz
32
32
  dynamic:
33
- checksum: b34d007f69eea87a197e5bc8b0cd22f512dc37b732c2ae8c49f053c70c291d2c
33
+ checksum: 1c037b8370b755d706340e25d3e4b2f4acb279dd03873cc53bcf0a6ec0832653
34
34
  filename: appsignal-i686-linux-all-dynamic.tar.gz
35
35
  i686-linux-musl:
36
36
  static:
37
- checksum: ad58fec875496aae76c870e5a1abdf37af27e4d237fc791c4820ef8ccfa05586
37
+ checksum: 0add9eed4452feda7fc5e1bbd0acdff32c353e4ea0b5d527959df57deb1bdcb2
38
38
  filename: appsignal-i686-linux-musl-all-static.tar.gz
39
39
  x86-linux-musl:
40
40
  static:
41
- checksum: ad58fec875496aae76c870e5a1abdf37af27e4d237fc791c4820ef8ccfa05586
41
+ checksum: 0add9eed4452feda7fc5e1bbd0acdff32c353e4ea0b5d527959df57deb1bdcb2
42
42
  filename: appsignal-i686-linux-musl-all-static.tar.gz
43
43
  x86_64-linux:
44
44
  static:
45
- checksum: dcab7852e81cbacb0a187cb62ce2f583f3ff835fb7c49c09f4e41df64b4b81c5
45
+ checksum: d11221c127c00128da16b419c503281407e429c0ea6f5bfe1691640b8e995e4e
46
46
  filename: appsignal-x86_64-linux-all-static.tar.gz
47
47
  dynamic:
48
- checksum: 33523662808254ae1e630e33a2466bf17acfb636e3726d98d11b375619ea2cdf
48
+ checksum: 6869ab461fde55487d55805c396d55f36cb881998556f44236035b949939b0af
49
49
  filename: appsignal-x86_64-linux-all-dynamic.tar.gz
50
50
  x86_64-linux-musl:
51
51
  static:
52
- checksum: 63cb4ac3d8befaec47eb907b1ff4c6c4af93e39fd7696db783cb6e656dda297c
52
+ checksum: 7ce44dc23c578933ca37a79d244bc367fdc2438408c2a61558adb92bcfebb1fa
53
53
  filename: appsignal-x86_64-linux-musl-all-static.tar.gz
54
54
  dynamic:
55
- checksum: 4116c2757c2be2b8eb259d787034ec4ab4123889735ded1ef584afbd68a8247a
55
+ checksum: 78d98f468e3a12cc09baff9e68bc4d9cd3b79f4a3bbe744036bff685415546a4
56
56
  filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
57
57
  x86_64-freebsd:
58
58
  static:
59
- checksum: 39c006dd131d1ca452ff79ec988688a69823e0abf26ee6966b1639cc42720416
59
+ checksum: df5f8b61e6ecca40f349cf5c83d5f37f031850d367793dee90dc56f13974431d
60
60
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
61
61
  dynamic:
62
- checksum: 937fb029f9f6a57aa818d46dcf8fbbfe06481c0765ad591859fbc6970a32a1df
62
+ checksum: 30d0303e97386014640c5b8194b777a5741e08ab5497ba58a7d8229bd4890fc5
63
63
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
64
64
  amd64-freebsd:
65
65
  static:
66
- checksum: 39c006dd131d1ca452ff79ec988688a69823e0abf26ee6966b1639cc42720416
66
+ checksum: df5f8b61e6ecca40f349cf5c83d5f37f031850d367793dee90dc56f13974431d
67
67
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
68
68
  dynamic:
69
- checksum: 937fb029f9f6a57aa818d46dcf8fbbfe06481c0765ad591859fbc6970a32a1df
69
+ checksum: 30d0303e97386014640c5b8194b777a5741e08ab5497ba58a7d8229bd4890fc5
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
- require File.expand_path(File.join(Dir.pwd, "config/application.rb"))
78
+ name_overwritten = configure_rails_app_name(config)
79
+ configure(config, rails_environments, name_overwritten)
80
+ done_notice
81
+ end
79
82
 
80
- config[:name] = Appsignal::Utils::RailsHelper.detected_rails_app_name
81
- name_overwritten = yes_or_no(" Your app's name is: '#{config[:name]}' \n Do you want to change how this is displayed in AppSignal? (y/n): ")
82
- puts
83
- if name_overwritten
84
- config[:name] = required_input(" Choose app's display name: ")
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
- out << :rails if framework_available? "rails"
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"
@@ -39,7 +39,8 @@ module Appsignal
39
39
  :enable_minutely_probes => true,
40
40
  :ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
41
41
  :dns_servers => [],
42
- :files_world_accessible => true
42
+ :files_world_accessible => true,
43
+ :transaction_debug_mode => false
43
44
  }.freeze
44
45
 
45
46
  ENV_TO_KEY_MAPPING = {
@@ -75,6 +76,7 @@ module Appsignal
75
76
  "APPSIGNAL_DNS_SERVERS" => :dns_servers,
76
77
  "APPSIGNAL_FILES_WORLD_ACCESSIBLE" => :files_world_accessible,
77
78
  "APPSIGNAL_REQUEST_HEADERS" => :request_headers,
79
+ "APPSIGNAL_TRANSACTION_DEBUG_MODE" => :transaction_debug_mode,
78
80
  "APP_REVISION" => :revision
79
81
  }.freeze
80
82
 
@@ -219,6 +221,7 @@ module Appsignal
219
221
  ENV["_APPSIGNAL_CA_FILE_PATH"] = config_hash[:ca_file_path].to_s
220
222
  ENV["_APPSIGNAL_DNS_SERVERS"] = config_hash[:dns_servers].join(",")
221
223
  ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = config_hash[:files_world_accessible].to_s
224
+ ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"] = config_hash[:transaction_debug_mode].to_s
222
225
  ENV["_APP_REVISION"] = config_hash[:revision].to_s
223
226
  end
224
227
 
@@ -324,7 +327,7 @@ module Appsignal
324
327
  APPSIGNAL_ENABLE_ALLOCATION_TRACKING APPSIGNAL_ENABLE_GC_INSTRUMENTATION
325
328
  APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
326
329
  APPSIGNAL_SEND_PARAMS APPSIGNAL_ENABLE_MINUTELY_PROBES
327
- APPSIGNAL_FILES_WORLD_ACCESSIBLE].each do |var|
330
+ APPSIGNAL_FILES_WORLD_ACCESSIBLE APPSIGNAL_TRANSACTION_DEBUG_MODE].each do |var|
328
331
  env_var = ENV[var]
329
332
  next unless env_var
330
333
  config[ENV_TO_KEY_MAPPING[var]] = env_var.casecmp("true").zero?
@@ -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
@@ -11,6 +11,10 @@ module Appsignal
11
11
  rails_class.parent_name
12
12
  end
13
13
  end
14
+
15
+ def self.application_config_path
16
+ File.expand_path(File.join(Dir.pwd, "config/application.rb"))
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.9.16".freeze
4
+ VERSION = "2.9.18".freeze
5
5
  end
@@ -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
 
@@ -134,6 +134,7 @@ describe Appsignal::Config do
134
134
  :ca_file_path => File.join(resources_dir, "cacert.pem"),
135
135
  :dns_servers => [],
136
136
  :files_world_accessible => true,
137
+ :transaction_debug_mode => false,
137
138
  :revision => "v2.5.1",
138
139
  :request_headers => []
139
140
  )
@@ -473,6 +474,7 @@ describe Appsignal::Config do
473
474
  config[:filter_parameters] = %w[password confirm_password]
474
475
  config[:running_in_container] = false
475
476
  config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
477
+ config[:transaction_debug_mode] = true
476
478
  config[:revision] = "v2.5.1"
477
479
  config.write_to_environment
478
480
  end
@@ -500,6 +502,7 @@ describe Appsignal::Config do
500
502
  expect(ENV["_APPSIGNAL_CA_FILE_PATH"]).to eq File.join(resources_dir, "cacert.pem")
501
503
  expect(ENV["_APPSIGNAL_DNS_SERVERS"]).to eq "8.8.8.8,8.8.4.4"
502
504
  expect(ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]).to eq "true"
505
+ expect(ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"]).to eq "true"
503
506
  expect(ENV["_APP_REVISION"]).to eq "v2.5.1"
504
507
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIR_PATH")
505
508
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIRECTORY_PATH")
@@ -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
- it "adds the Puma minutely probe" do
38
- probe = Appsignal::Minutely.probes[:puma]
39
- expect(probe).to eql(Appsignal::Hooks::PumaProbe)
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
- it "adds the Puma minutely probe" do
66
- probe = Appsignal::Minutely.probes[:puma]
67
- expect(probe).to eql(Appsignal::Hooks::PumaProbe)
68
- end
69
- end
70
- end
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
- it "should add a before shutdown worker callback" do
81
- expect(Puma.cli_config.options[:before_fork].first).to be_a(Proc)
82
- expect(Puma.cli_config.options[:before_worker_boot].first).to be_a(Proc)
83
- expect(Puma.cli_config.options[:before_worker_shutdown].first).to be_a(Proc)
84
- end
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
- context "with existing hooks" do
88
- before do
89
- Puma.cli_config.options[:before_fork] = []
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
- it "should add a before shutdown worker callback" do
96
- expect(Puma.cli_config.options[:before_fork].first).to be_a(Proc)
97
- expect(Puma.cli_config.options[:before_worker_boot].first).to be_a(Proc)
98
- expect(Puma.cli_config.options[:before_worker_shutdown].first).to be_a(Proc)
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 Probe
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 < Probe
18
+ class ProbeWithMissingDependency < MockProbe
29
19
  def self.dependencies_present?
30
20
  false
31
21
  end
32
22
  end
33
23
 
34
- class BrokenProbe < Probe
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 < Probe
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 = Probe.new
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 = Probe
94
- probe_instance = Probe.new
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 = Probe.new
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 = Probe.new
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
@@ -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
@@ -0,0 +1,11 @@
1
+ class MockProbe
2
+ attr_reader :calls
3
+
4
+ def initialize
5
+ @calls = 0
6
+ end
7
+
8
+ def call
9
+ @calls += 1
10
+ end
11
+ 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.16
4
+ version: 2.9.18
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-09-30 00:00:00.000000000 Z
13
+ date: 2019-11-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -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.2
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