appsignal 2.11.0.beta.4 → 2.11.0.beta.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +57 -1
  3. data/CHANGELOG.md +12 -0
  4. data/README.md +4 -4
  5. data/Rakefile +16 -4
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +2 -2
  8. data/ext/Rakefile +2 -0
  9. data/ext/agent.yml +19 -19
  10. data/ext/base.rb +7 -0
  11. data/ext/extconf.rb +2 -0
  12. data/lib/appsignal.rb +1 -0
  13. data/lib/appsignal/auth_check.rb +4 -2
  14. data/lib/appsignal/cli/diagnose.rb +1 -1
  15. data/lib/appsignal/config.rb +35 -2
  16. data/lib/appsignal/extension.rb +6 -5
  17. data/lib/appsignal/extension/jruby.rb +6 -5
  18. data/lib/appsignal/hooks.rb +23 -0
  19. data/lib/appsignal/hooks/active_job.rb +32 -9
  20. data/lib/appsignal/hooks/puma.rb +0 -1
  21. data/lib/appsignal/hooks/sidekiq.rb +0 -1
  22. data/lib/appsignal/probes.rb +7 -0
  23. data/lib/appsignal/probes/puma.rb +1 -1
  24. data/lib/appsignal/probes/sidekiq.rb +3 -1
  25. data/lib/appsignal/utils/deprecation_message.rb +1 -1
  26. data/lib/appsignal/version.rb +1 -1
  27. data/spec/lib/appsignal/auth_check_spec.rb +23 -0
  28. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  29. data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
  30. data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
  31. data/spec/lib/appsignal/config_spec.rb +21 -0
  32. data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
  33. data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
  34. data/spec/lib/appsignal/hooks/activejob_spec.rb +44 -1
  35. data/spec/lib/appsignal/hooks_spec.rb +57 -0
  36. data/spec/lib/appsignal/marker_spec.rb +1 -1
  37. data/spec/spec_helper.rb +5 -0
  38. data/spec/support/helpers/config_helpers.rb +3 -2
  39. data/spec/support/helpers/transaction_helpers.rb +1 -1
  40. data/spec/support/testing.rb +19 -19
  41. metadata +6 -3
@@ -0,0 +1,7 @@
1
+ module Appsignal
2
+ module Probes
3
+ end
4
+ end
5
+
6
+ require "appsignal/probes/puma"
7
+ require "appsignal/probes/sidekiq"
@@ -1,11 +1,11 @@
1
1
  module Appsignal
2
2
  module Probes
3
- # @api private
4
3
  class PumaProbe
5
4
  def initialize
6
5
  @hostname = Appsignal.config[:hostname] || Socket.gethostname
7
6
  end
8
7
 
8
+ # @api private
9
9
  def call
10
10
  puma_stats = fetch_puma_stats
11
11
  return unless puma_stats
@@ -1,9 +1,10 @@
1
1
  module Appsignal
2
2
  module Probes
3
- # @api private
4
3
  class SidekiqProbe
4
+ # @api private
5
5
  attr_reader :config
6
6
 
7
+ # @api private
7
8
  def self.dependencies_present?
8
9
  Gem::Version.new(::Redis::VERSION) >= Gem::Version.new("3.3.5")
9
10
  end
@@ -16,6 +17,7 @@ module Appsignal
16
17
  require "sidekiq/api"
17
18
  end
18
19
 
20
+ # @api private
19
21
  def call
20
22
  track_redis_info
21
23
  track_stats
@@ -2,7 +2,7 @@ module Appsignal
2
2
  module Utils
3
3
  module DeprecationMessage
4
4
  def self.message(message, logger = Appsignal.logger)
5
- $stderr.puts "appsignal WARNING: #{message}"
5
+ Kernel.warn "appsignal WARNING: #{message}"
6
6
  logger.warn message
7
7
  end
8
8
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.0.beta.4".freeze
4
+ VERSION = "2.11.0.beta.5".freeze
5
5
  end
@@ -28,6 +28,29 @@ describe Appsignal::AuthCheck do
28
28
  end.join("&")
29
29
  end
30
30
 
31
+ describe ".new" do
32
+ describe "with logger argument" do
33
+ let(:err_stream) { std_stream }
34
+ let(:stderr) { err_stream.read }
35
+ let(:log_stream) { std_stream }
36
+ let(:log) { log_contents(log_stream) }
37
+
38
+ it "logs and prints a deprecation message" do
39
+ Appsignal.logger = test_logger(log_stream)
40
+
41
+ capture_std_streams(std_stream, err_stream) do
42
+ Appsignal::AuthCheck.new(config, Appsignal.logger)
43
+ end
44
+
45
+ deprecation_message =
46
+ "`Appsignal::AuthCheck.new`'s `logger` argument " \
47
+ "will be removed in the next major version."
48
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
49
+ expect(log).to contains_log :warn, deprecation_message
50
+ end
51
+ end
52
+ end
53
+
31
54
  describe "#perform" do
32
55
  subject { auth_check.perform }
33
56
 
@@ -10,7 +10,7 @@ if DependencyHelper.capistrano2_present?
10
10
  let(:capistrano_config) do
11
11
  Capistrano::Configuration.new.tap do |c|
12
12
  c.set(:rails_env, "production")
13
- c.set(:repository, "master")
13
+ c.set(:repository, "main")
14
14
  c.set(:deploy_to, "/home/username/app")
15
15
  c.set(:current_release, "")
16
16
  c.set(:current_revision, "503ce0923ed177a3ce000005")
@@ -15,7 +15,7 @@ if DependencyHelper.capistrano3_present?
15
15
  c.set(:log_level, :error)
16
16
  c.set(:logger, logger)
17
17
  c.set(:rails_env, "production")
18
- c.set(:repository, "master")
18
+ c.set(:repository, "main")
19
19
  c.set(:deploy_to, "/home/username/app")
20
20
  c.set(:current_release, "")
21
21
  c.set(:current_revision, "503ce0923ed177a3ce000005")
@@ -290,6 +290,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
290
290
  jruby = Appsignal::System.jruby?
291
291
  expect(output).to include(
292
292
  "Extension installation report",
293
+ "Installation result",
294
+ " Status: success",
293
295
  "Language details",
294
296
  " Implementation: #{jruby ? "jruby" : "ruby"}",
295
297
  " Ruby version: #{"#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"}",
@@ -310,6 +312,46 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
310
312
  )
311
313
  end
312
314
 
315
+ context "with error in install report" do
316
+ let(:error) { RuntimeError.new("some error") }
317
+ before do
318
+ allow(File).to receive(:read).and_call_original
319
+ expect(File).to receive(:read)
320
+ .with(File.expand_path("../../../../../ext/install.report", __FILE__))
321
+ .and_return(
322
+ YAML.dump(
323
+ "result" => {
324
+ "status" => "error",
325
+ "error" => "RuntimeError: some error",
326
+ "backtrace" => error.backtrace
327
+ }
328
+ )
329
+ )
330
+ end
331
+
332
+ it "sends an error" do
333
+ run
334
+ expect(received_report["installation"]).to match(
335
+ "result" => {
336
+ "status" => "error",
337
+ "error" => "RuntimeError: some error",
338
+ "backtrace" => error.backtrace
339
+ }
340
+ )
341
+ end
342
+
343
+ it "prints the error" do
344
+ run
345
+
346
+ expect(output).to include(
347
+ "Extension installation report",
348
+ "Installation result",
349
+ "Status: error\n Error: RuntimeError: some error"
350
+ )
351
+ expect(output).to_not include("Raw report:")
352
+ end
353
+ end
354
+
313
355
  context "without install report" do
314
356
  let(:error) { RuntimeError.new("foo") }
315
357
  before do
@@ -244,6 +244,27 @@ describe Appsignal::Config do
244
244
  end
245
245
  end
246
246
 
247
+ context "with an overriden config file" do
248
+ let(:config) do
249
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "appsignal.yml"))
250
+ end
251
+
252
+ it "is valid and active" do
253
+ expect(config.valid?).to be_truthy
254
+ expect(config.active?).to be_truthy
255
+ end
256
+
257
+ context "with an invalid overriden config file" do
258
+ let(:config) do
259
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "missing.yml"))
260
+ end
261
+
262
+ it "is not valid" do
263
+ expect(config.valid?).to be_falsy
264
+ end
265
+ end
266
+ end
267
+
247
268
  context "with the config file causing an error" do
248
269
  let(:config_path) do
249
270
  File.expand_path(
@@ -1,42 +1,45 @@
1
- if Appsignal::System.jruby?
2
- describe Appsignal::Extension::Jruby do
3
- let(:extension) { Appsignal::Extension }
1
+ describe "JRuby extension", :jruby do
2
+ let(:extension) { Appsignal::Extension }
3
+ let(:jruby_module) { Appsignal::Extension::Jruby }
4
4
 
5
- describe "string conversions" do
6
- it "keeps the same value during string type conversions" do
7
- # UTF-8 string with NULL
8
- # Tests if the conversions between the conversions without breaking on
9
- # NULL terminated strings in C.
10
- string = "Merry Christmas! \u0000 🎄"
5
+ it "creates a JRuby extension module" do
6
+ expect(Appsignal::Extension::Jruby).to be_kind_of(Module)
7
+ end
11
8
 
12
- appsignal_string = extension.make_appsignal_string(string)
13
- ruby_string = extension.make_ruby_string(appsignal_string)
9
+ describe "string conversions" do
10
+ it "keeps the same value during string type conversions" do
11
+ # UTF-8 string with NULL
12
+ # Tests if the conversions between the conversions without breaking on
13
+ # NULL terminated strings in C.
14
+ string = "Merry Christmas! \u0000 🎄"
14
15
 
15
- expect(ruby_string).to eq("Merry Christmas! \u0000 🎄")
16
- end
17
- end
16
+ appsignal_string = extension.make_appsignal_string(string)
17
+ ruby_string = extension.make_ruby_string(appsignal_string)
18
18
 
19
- it "loads libappsignal with FFI" do
20
- expect(described_class.ffi_libraries.map(&:name).first).to include "libappsignal"
19
+ expect(ruby_string).to eq("Merry Christmas! \u0000 🎄")
21
20
  end
21
+ end
22
22
 
23
- describe ".lib_extension" do
24
- subject { described_class.lib_extension }
23
+ it "loads libappsignal with FFI" do
24
+ expect(jruby_module.ffi_libraries.map(&:name).first).to include "libappsignal"
25
+ end
26
+
27
+ describe ".lib_extension" do
28
+ subject { jruby_module.lib_extension }
25
29
 
26
- context "when on a darwin system" do
27
- before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
30
+ context "when on a darwin system" do
31
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
28
32
 
29
- it "returns the extension for darwin" do
30
- is_expected.to eq "dylib"
31
- end
33
+ it "returns the extension for darwin" do
34
+ is_expected.to eq "dylib"
32
35
  end
36
+ end
33
37
 
34
- context "when on a linux system" do
35
- before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
38
+ context "when on a linux system" do
39
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
36
40
 
37
- it "returns the lib extension for linux" do
38
- is_expected.to eq "so"
39
- end
41
+ it "returns the lib extension for linux" do
42
+ is_expected.to eq "so"
40
43
  end
41
44
  end
42
45
  end
@@ -0,0 +1,23 @@
1
+ describe Appsignal::Extension, :extension_installation_failure do
2
+ context "when the extension library cannot be loaded" do
3
+ # This test breaks the installation on purpose and is not run by default.
4
+ # See `rake test:failure`. If this test was run, run `rake
5
+ # extension:install` again to fix the extension installation.
6
+ it "prints and logs an error" do
7
+ # ENV var to make sure installation fails on purpurse
8
+ ENV["_TEST_APPSIGNAL_EXTENSION_FAILURE"] = "true"
9
+ `rake extension:install` # Run installation
10
+
11
+ require "open3"
12
+ _stdout, stderr, _status = Open3.capture3("bin/appsignal --version")
13
+ expect(stderr).to include("ERROR: AppSignal failed to load extension")
14
+ error_message =
15
+ if DependencyHelper.running_jruby?
16
+ "cannot open shared object file"
17
+ else
18
+ "LoadError: cannot load such file"
19
+ end
20
+ expect(stderr).to include(error_message)
21
+ end
22
+ end
23
+ end
@@ -154,9 +154,11 @@ if DependencyHelper.active_job_present?
154
154
  end
155
155
 
156
156
  it "reports the priority as tag on the transaction" do
157
- tags = { :priority => 10, :queue => queue }
157
+ tags = { :queue => queue }
158
158
  expect(Appsignal).to receive(:increment_counter)
159
159
  .with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
160
+ expect(Appsignal).to receive(:increment_counter)
161
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
160
162
 
161
163
  perform_job(ActiveJobPriorityTestJob)
162
164
 
@@ -208,6 +210,47 @@ if DependencyHelper.active_job_present?
208
210
  .map { |event| event["name"] }
209
211
  expect(events).to eq(["perform_start.active_job", "perform.active_job"])
210
212
  end
213
+
214
+ if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
215
+ context "with priority" do
216
+ before do
217
+ class ActiveJobErrorPriorityTestJob < ActiveJob::Base
218
+ queue_with_priority 10
219
+
220
+ def perform(*_args)
221
+ raise "uh oh"
222
+ end
223
+ end
224
+ end
225
+ after do
226
+ Object.send(:remove_const, :ActiveJobErrorPriorityTestJob)
227
+ end
228
+
229
+ it "reports the priority as tag on the transaction" do
230
+ tags = { :queue => queue }
231
+ expect(Appsignal).to receive(:increment_counter)
232
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
233
+ expect(Appsignal).to receive(:increment_counter)
234
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :failed))
235
+ expect(Appsignal).to receive(:increment_counter)
236
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
237
+ expect(Appsignal).to receive(:increment_counter)
238
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :failed))
239
+
240
+ expect do
241
+ perform_job(ActiveJobErrorPriorityTestJob)
242
+ end.to raise_error(RuntimeError, "uh oh")
243
+
244
+ transaction = last_transaction
245
+ transaction_hash = transaction.to_h
246
+ expect(transaction_hash).to include(
247
+ "sample_data" => hash_including(
248
+ "tags" => hash_including("queue" => queue, "priority" => 10)
249
+ )
250
+ )
251
+ end
252
+ end
253
+ end
211
254
  end
212
255
 
213
256
  context "when wrapped in another transaction" do
@@ -78,6 +78,63 @@ describe Appsignal::Hooks do
78
78
  expect(Appsignal::Hooks.hooks[:mock_error_hook].installed?).to be_falsy
79
79
  Appsignal::Hooks.hooks.delete(:mock_error_hook)
80
80
  end
81
+
82
+ describe "missing constants" do
83
+ let(:err_stream) { std_stream }
84
+ let(:stderr) { err_stream.read }
85
+ let(:log_stream) { std_stream }
86
+ let(:log) { log_contents(log_stream) }
87
+ before do
88
+ Appsignal.logger = test_logger(log_stream)
89
+ end
90
+
91
+ def call_constant(&block)
92
+ capture_std_streams(std_stream, err_stream, &block)
93
+ end
94
+
95
+ describe "SidekiqProbe" do
96
+ it "logs a deprecation message and returns the new constant" do
97
+ constant = call_constant { Appsignal::Hooks::SidekiqProbe }
98
+
99
+ expect(constant).to eql(Appsignal::Probes::SidekiqProbe)
100
+ expect(constant.name).to eql("Appsignal::Probes::SidekiqProbe")
101
+
102
+ deprecation_message =
103
+ "The constant Appsignal::Hooks::SidekiqProbe has been deprecated. " \
104
+ "Please update the constant name to Appsignal::Probes::SidekiqProbe " \
105
+ "in the following file to remove this message.\n#{__FILE__}:"
106
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
107
+ expect(log).to contains_log :warn, deprecation_message
108
+ end
109
+ end
110
+
111
+ describe "PumaProbe" do
112
+ it "logs a deprecation message and returns the new constant" do
113
+ constant = call_constant { Appsignal::Hooks::PumaProbe }
114
+
115
+ expect(constant).to eql(Appsignal::Probes::PumaProbe)
116
+ expect(constant.name).to eql("Appsignal::Probes::PumaProbe")
117
+
118
+ deprecation_message =
119
+ "The constant Appsignal::Hooks::PumaProbe has been deprecated. " \
120
+ "Please update the constant name to Appsignal::Probes::PumaProbe " \
121
+ "in the following file to remove this message.\n#{__FILE__}:"
122
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
123
+ expect(log).to contains_log :warn, deprecation_message
124
+ end
125
+ end
126
+
127
+ describe "other constant" do
128
+ it "raises a NameError like Ruby normally does" do
129
+ expect do
130
+ call_constant { Appsignal::Hooks::Unknown }
131
+ end.to raise_error(NameError)
132
+
133
+ expect(stderr).to be_empty
134
+ expect(log).to be_empty
135
+ end
136
+ end
137
+ end
81
138
  end
82
139
 
83
140
  describe Appsignal::Hooks::Helpers do
@@ -4,7 +4,7 @@ describe Appsignal::Marker do
4
4
  described_class.new(
5
5
  {
6
6
  :revision => "503ce0923ed177a3ce000005",
7
- :repository => "master",
7
+ :repository => "main",
8
8
  :user => "batman",
9
9
  :rails_env => "production"
10
10
  },
@@ -65,6 +65,10 @@ RSpec.configure do |config|
65
65
 
66
66
  config.example_status_persistence_file_path = "spec/examples.txt"
67
67
  config.fail_if_no_examples = true
68
+ config.filter_run_excluding(
69
+ :extension_installation_failure => true,
70
+ :jruby => !DependencyHelper.running_jruby?
71
+ )
68
72
  config.mock_with :rspec do |mocks|
69
73
  mocks.syntax = :expect
70
74
  end
@@ -111,6 +115,7 @@ RSpec.configure do |config|
111
115
  # in the diagnose task, so add it manually to the list of to-be cleaned up
112
116
  # keys.
113
117
  env_keys << "_APPSIGNAL_DIAGNOSE"
118
+ env_keys << "_TEST_APPSIGNAL_EXTENSION_FAILURE"
114
119
  env_keys.each do |key|
115
120
  # First set the ENV var to an empty string and then delete the key from
116
121
  # the env. We set the env var to an empty string first as JRuby doesn't