appsignal 2.4.3 → 2.5.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -5
  3. data/.rubocop_todo.yml +3 -0
  4. data/.travis.yml +3 -4
  5. data/CHANGELOG.md +3 -0
  6. data/Rakefile +21 -6
  7. data/appsignal.gemspec +12 -5
  8. data/ext/Rakefile +27 -0
  9. data/ext/agent.yml +52 -21
  10. data/ext/base.rb +79 -0
  11. data/ext/extconf.rb +5 -76
  12. data/gemfiles/sequel-435.gemfile +5 -1
  13. data/gemfiles/sequel.gemfile +5 -1
  14. data/lib/appsignal.rb +11 -6
  15. data/lib/appsignal/cli.rb +1 -2
  16. data/lib/appsignal/cli/install.rb +3 -3
  17. data/lib/appsignal/config.rb +56 -37
  18. data/lib/appsignal/extension.rb +28 -4
  19. data/lib/appsignal/extension/jruby.rb +460 -0
  20. data/lib/appsignal/hooks/sidekiq.rb +4 -4
  21. data/lib/appsignal/integrations/capistrano/appsignal.cap +7 -2
  22. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +8 -3
  23. data/lib/appsignal/system.rb +16 -1
  24. data/lib/appsignal/transaction.rb +2 -2
  25. data/lib/appsignal/utils.rb +3 -1
  26. data/lib/appsignal/version.rb +1 -3
  27. data/spec/.rubocop.yml +3 -0
  28. data/spec/lib/appsignal/capistrano2_spec.rb +55 -41
  29. data/spec/lib/appsignal/capistrano3_spec.rb +87 -61
  30. data/spec/lib/appsignal/cli/diagnose_spec.rb +3 -3
  31. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +4 -4
  32. data/spec/lib/appsignal/config_spec.rb +54 -21
  33. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  34. data/spec/lib/appsignal/extension_spec.rb +28 -12
  35. data/spec/lib/appsignal/hooks/sequel_spec.rb +7 -1
  36. data/spec/lib/appsignal/integrations/que_spec.rb +5 -5
  37. data/spec/lib/appsignal/system_spec.rb +5 -4
  38. data/spec/lib/appsignal/transaction_spec.rb +8 -8
  39. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +4 -4
  40. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +3 -3
  41. data/spec/lib/appsignal_spec.rb +5 -3
  42. data/spec/spec_helper.rb +29 -8
  43. data/spec/support/helpers/config_helpers.rb +3 -2
  44. metadata +12 -7
@@ -260,12 +260,12 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
260
260
  "Agent diagnostics",
261
261
  " Error while parsing agent diagnostics report:",
262
262
  " Output: invalid agent\njson"
263
- expect(output).to match(/Error: \d+: unexpected token at 'invalid agent\njson'/)
263
+ expect(output).to match(/Error:( \d+:)? unexpected token at 'invalid agent\njson'/)
264
264
  end
265
265
 
266
266
  it "adds the output to the report" do
267
267
  expect(received_report["agent"]["error"])
268
- .to match(/\d+: unexpected token at 'invalid agent\njson'/)
268
+ .to match(/unexpected token at 'invalid agent\njson'/)
269
269
  expect(received_report["agent"]["output"]).to eq(["invalid agent", "json"])
270
270
  end
271
271
  end
@@ -652,7 +652,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
652
652
  it "adds paths to the report" do
653
653
  run
654
654
  expect(received_report["paths"].keys)
655
- .to match_array(%w(root_path working_dir log_dir_path log_file_path))
655
+ .to match_array(%w[root_path working_dir log_dir_path log_file_path])
656
656
  end
657
657
  end
658
658
 
@@ -75,7 +75,7 @@ describe Appsignal::CLI::NotifyOfDeploy do
75
75
 
76
76
  it "requires environment option" do
77
77
  expect { run }.to raise_error(SystemExit)
78
- expect(output).to include_missing_options(%w(environment))
78
+ expect(output).to include_missing_options(%w[environment])
79
79
  expect(output).to_not include_deploy_notification
80
80
  end
81
81
  end
@@ -98,7 +98,7 @@ describe Appsignal::CLI::NotifyOfDeploy do
98
98
  it "prints a missing required options error" do
99
99
  expect { run }.to raise_error(SystemExit)
100
100
  expect(output).to_not include_config_error
101
- expect(output).to include_missing_options(%w(revision user))
101
+ expect(output).to include_missing_options(%w[revision user])
102
102
  expect(output).to_not include_deploy_notification
103
103
  end
104
104
 
@@ -108,7 +108,7 @@ describe Appsignal::CLI::NotifyOfDeploy do
108
108
  it "prints a missing required option error" do
109
109
  expect { run }.to raise_error(SystemExit)
110
110
  expect(output).to_not include_config_error
111
- expect(output).to include_missing_options(%w(user))
111
+ expect(output).to include_missing_options(%w[user])
112
112
  expect(output).to_not include_deploy_notification
113
113
  end
114
114
  end
@@ -133,7 +133,7 @@ describe Appsignal::CLI::NotifyOfDeploy do
133
133
  it "requires name option" do
134
134
  expect { run }.to raise_error(SystemExit)
135
135
  expect(output).to_not include_config_error
136
- expect(output).to include_missing_options(%w(name))
136
+ expect(output).to include_missing_options(%w[name])
137
137
  expect(output).to_not include_deploy_notification
138
138
  end
139
139
  end
@@ -222,43 +222,54 @@ describe Appsignal::Config do
222
222
  end
223
223
  end
224
224
 
225
- describe "old-style config keys" do
226
- describe ":api_key" do
227
- subject { config[:push_api_key] }
225
+ describe "support for old config keys" do
226
+ let(:config) { project_fixture_config(env, {}, test_logger(log)) }
227
+ let(:log) { StringIO.new }
228
228
 
229
+ describe ":api_key" do
229
230
  context "without :push_api_key" do
230
- let(:config) { project_fixture_config("old_config") }
231
+ let(:env) { "old_config" }
231
232
 
232
233
  it "sets the :push_api_key with the old :api_key value" do
233
- expect(subject).to eq "def"
234
+ expect(config[:push_api_key]).to eq "def"
235
+ expect(config.config_hash).to_not have_key :api_key
236
+ expect(log_contents(log)).to contains_log :warn,
237
+ "Old configuration key found. Please update the 'api_key' to 'push_api_key'"
234
238
  end
235
239
  end
236
240
 
237
241
  context "with :push_api_key" do
238
- let(:config) { project_fixture_config("old_config_mixed_with_new_config") }
242
+ let(:env) { "old_config_mixed_with_new_config" }
239
243
 
240
- it "ignores the :api_key config" do
241
- expect(subject).to eq "ghi"
244
+ it "ignores the :api_key config and deletes it" do
245
+ expect(config[:push_api_key]).to eq "ghi"
246
+ expect(config.config_hash).to_not have_key :api_key
247
+ expect(log_contents(log)).to contains_log :warn,
248
+ "Old configuration key found. Please update the 'api_key' to 'push_api_key'"
242
249
  end
243
250
  end
244
251
  end
245
252
 
246
253
  describe ":ignore_exceptions" do
247
- subject { config[:ignore_errors] }
248
-
249
254
  context "without :ignore_errors" do
250
- let(:config) { project_fixture_config("old_config") }
255
+ let(:env) { "old_config" }
251
256
 
252
257
  it "sets :ignore_errors with the old :ignore_exceptions value" do
253
- expect(subject).to eq ["StandardError"]
258
+ expect(config[:ignore_errors]).to eq ["StandardError"]
259
+ expect(config.config_hash).to_not have_key :ignore_exceptions
260
+ expect(log_contents(log)).to contains_log :warn,
261
+ "Old configuration key found. Please update the 'ignore_exceptions' to 'ignore_errors'"
254
262
  end
255
263
  end
256
264
 
257
265
  context "with :ignore_errors" do
258
- let(:config) { project_fixture_config("old_config_mixed_with_new_config") }
266
+ let(:env) { "old_config_mixed_with_new_config" }
259
267
 
260
268
  it "ignores the :ignore_exceptions config" do
261
- expect(subject).to eq ["NoMethodError"]
269
+ expect(config[:ignore_errors]).to eq ["NoMethodError"]
270
+ expect(config.config_hash).to_not have_key :ignore_exceptions
271
+ expect(log_contents(log)).to contains_log :warn,
272
+ "Old configuration key found. Please update the 'ignore_exceptions' to 'ignore_errors'"
262
273
  end
263
274
  end
264
275
  end
@@ -298,9 +309,9 @@ describe Appsignal::Config do
298
309
  expect(config[:active]).to eq(true)
299
310
  expect(config[:name]).to eq "App name"
300
311
  expect(config[:debug]).to eq(true)
301
- expect(config[:ignore_actions]).to eq %w(action1 action2)
302
- expect(config[:ignore_errors]).to eq %w(ExampleStandardError AnotherError)
303
- expect(config[:ignore_namespaces]).to eq %w(admin private_namespace)
312
+ expect(config[:ignore_actions]).to eq %w[action1 action2]
313
+ expect(config[:ignore_errors]).to eq %w[ExampleStandardError AnotherError]
314
+ expect(config[:ignore_namespaces]).to eq %w[admin private_namespace]
304
315
  expect(config[:instrument_net_http]).to eq(false)
305
316
  expect(config[:instrument_redis]).to eq(false)
306
317
  expect(config[:instrument_sequel]).to eq(false)
@@ -384,13 +395,13 @@ describe Appsignal::Config do
384
395
  let(:config) { project_fixture_config(:production) }
385
396
  before do
386
397
  config[:http_proxy] = "http://localhost"
387
- config[:ignore_actions] = %w(action1 action2)
388
- config[:ignore_errors] = %w(ExampleStandardError AnotherError)
389
- config[:ignore_namespaces] = %w(admin private_namespace)
398
+ config[:ignore_actions] = %w[action1 action2]
399
+ config[:ignore_errors] = %w[ExampleStandardError AnotherError]
400
+ config[:ignore_namespaces] = %w[admin private_namespace]
390
401
  config[:log] = "stdout"
391
402
  config[:log_path] = "/tmp"
392
403
  config[:hostname] = "app1.local"
393
- config[:filter_parameters] = %w(password confirm_password)
404
+ config[:filter_parameters] = %w[password confirm_password]
394
405
  config[:running_in_container] = false
395
406
  config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
396
407
  config.write_to_environment
@@ -601,4 +612,26 @@ describe Appsignal::Config do
601
612
  end
602
613
  end
603
614
  end
615
+
616
+ describe "#validate" do
617
+ before { config.validate }
618
+ subject { config.valid? }
619
+ let(:config) { described_class.new(Dir.pwd, "production", :push_api_key => push_api_key) }
620
+
621
+ context "with missing push_api_key" do
622
+ let(:push_api_key) { nil }
623
+
624
+ it "sets valid to false" do
625
+ is_expected.to eq(false)
626
+ end
627
+ end
628
+
629
+ context "with push_api_key present" do
630
+ let(:push_api_key) { "abc" }
631
+
632
+ it "sets valid to true" do
633
+ is_expected.to eq(true)
634
+ end
635
+ end
636
+ end
604
637
  end
@@ -0,0 +1,43 @@
1
+ if Appsignal::System.jruby?
2
+ describe Appsignal::Extension::Jruby do
3
+ let(:extension) { Appsignal::Extension }
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 🎄"
11
+
12
+ appsignal_string = extension.make_appsignal_string(string)
13
+ ruby_string = extension.make_ruby_string(appsignal_string)
14
+
15
+ expect(ruby_string).to eq("Merry Christmas! \u0000 🎄")
16
+ end
17
+ end
18
+
19
+ it "loads libappsignal with FFI" do
20
+ expect(described_class.ffi_libraries.map(&:name).first).to include "libappsignal"
21
+ end
22
+
23
+ describe ".lib_extension" do
24
+ subject { described_class.lib_extension }
25
+
26
+ context "when on a darwin system" do
27
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
28
+
29
+ it "returns the extension for darwin" do
30
+ is_expected.to eq "dylib"
31
+ end
32
+ end
33
+
34
+ context "when on a linux system" do
35
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
36
+
37
+ it "returns the lib extension for linux" do
38
+ is_expected.to eq "so"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,4 @@
1
- require "fileutils"
2
-
3
- describe "extension loading and operation" do
1
+ describe Appsignal::Extension do
4
2
  describe ".agent_config" do
5
3
  subject { Appsignal::Extension.agent_config }
6
4
 
@@ -11,7 +9,7 @@ describe "extension loading and operation" do
11
9
  describe ".agent_version" do
12
10
  subject { Appsignal::Extension.agent_version }
13
11
 
14
- it { is_expected.to_not be_nil }
12
+ it { is_expected.to be_kind_of(String) }
15
13
  end
16
14
 
17
15
  context "when the extension library can be loaded" do
@@ -21,9 +19,29 @@ describe "extension loading and operation" do
21
19
  expect(Appsignal.extension_loaded?).to be_truthy
22
20
  end
23
21
 
24
- it "should have a start and stop method" do
25
- subject.start
26
- subject.stop
22
+ context "without valid config" do
23
+ let(:out_stream) { std_stream }
24
+ let(:output) { out_stream.read }
25
+
26
+ describe ".start" do
27
+ it "outputs a warning about not starting the extension" do
28
+ capture_std_streams(out_stream, out_stream) do
29
+ subject.start
30
+ end
31
+
32
+ expect(output).to include \
33
+ "WARNING: Error when reading appsignal config, appsignal not starting"
34
+ end
35
+ end
36
+
37
+ describe ".stop" do
38
+ it "does nothing" do
39
+ capture_std_streams(out_stream, out_stream) do
40
+ subject.stop
41
+ end
42
+ expect(output).to be_empty
43
+ end
44
+ end
27
45
  end
28
46
 
29
47
  context "with a valid config" do
@@ -101,11 +119,9 @@ describe "extension loading and operation" do
101
119
  context "when the extension library cannot be loaded" do
102
120
  subject { Appsignal::Extension }
103
121
 
104
- before :context do
105
- Appsignal.extension_loaded = false
106
- end
107
- after :context do
108
- Appsignal.extension_loaded = true
122
+ before do
123
+ allow(Appsignal).to receive(:extension_loaded).and_return(false)
124
+ allow(Appsignal).to receive(:testing?).and_return(false)
109
125
  end
110
126
 
111
127
  it "should indicate that the extension is not loaded" do
@@ -1,6 +1,12 @@
1
1
  describe Appsignal::Hooks::SequelHook do
2
2
  if DependencyHelper.sequel_present?
3
- let(:db) { Sequel.sqlite }
3
+ let(:db) do
4
+ if Appsignal::System.jruby?
5
+ Sequel.connect("jdbc:sqlite::memory:")
6
+ else
7
+ Sequel.sqlite
8
+ end
9
+ end
4
10
 
5
11
  before :context do
6
12
  start_agent
@@ -9,7 +9,7 @@ if DependencyHelper.que_present?
9
9
  :queue => "dfl",
10
10
  :job_class => "MyQueJob",
11
11
  :priority => 100,
12
- :args => %w(1 birds),
12
+ :args => %w[1 birds],
13
13
  :run_at => fixed_time,
14
14
  :error_count => 0
15
15
  }
@@ -26,7 +26,7 @@ if DependencyHelper.que_present?
26
26
  :run_at => fixed_time.to_s,
27
27
  :attempts => 0
28
28
  },
29
- :params => %w(1 birds)
29
+ :params => %w[1 birds]
30
30
  }
31
31
  end
32
32
 
@@ -92,7 +92,7 @@ if DependencyHelper.que_present?
92
92
  "title" => ""
93
93
  )
94
94
  expect(subject["sample_data"]).to include(
95
- "params" => %w(1 birds),
95
+ "params" => %w[1 birds],
96
96
  "metadata" => {
97
97
  "attempts" => 0,
98
98
  "id" => 123,
@@ -125,7 +125,7 @@ if DependencyHelper.que_present?
125
125
  "message" => error.message
126
126
  )
127
127
  expect(subject["sample_data"]).to include(
128
- "params" => %w(1 birds),
128
+ "params" => %w[1 birds],
129
129
  "metadata" => {
130
130
  "attempts" => 0,
131
131
  "id" => 123,
@@ -158,7 +158,7 @@ if DependencyHelper.que_present?
158
158
  "message" => error.message
159
159
  )
160
160
  expect(subject["sample_data"]).to include(
161
- "params" => %w(1 birds),
161
+ "params" => %w[1 birds],
162
162
  "metadata" => {
163
163
  "attempts" => 0,
164
164
  "id" => 123,
@@ -55,11 +55,12 @@ describe Appsignal::System do
55
55
  end
56
56
 
57
57
  describe ".agent_platform" do
58
- let(:os) { "linux" }
58
+ let(:os) { "linux-gnu" }
59
59
  let(:ldd_output) { "" }
60
60
  before do
61
61
  allow(described_class).to receive(:ldd_version_output).and_return(ldd_output)
62
- allow(Gem::Platform.local).to receive(:os).and_return(os)
62
+ allow(RbConfig::CONFIG).to receive(:[])
63
+ allow(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return(os)
63
64
  end
64
65
  subject { described_class.agent_platform }
65
66
 
@@ -110,7 +111,7 @@ describe Appsignal::System do
110
111
  end
111
112
 
112
113
  context "when on macOS" do
113
- let(:os) { "darwin" }
114
+ let(:os) { "darwin16.7.0" }
114
115
  let(:ldd_output) { "ldd: command not found" }
115
116
 
116
117
  it "returns the darwin build" do
@@ -119,7 +120,7 @@ describe Appsignal::System do
119
120
  end
120
121
 
121
122
  context "when on FreeBSD" do
122
- let(:os) { "freebsd" }
123
+ let(:os) { "freebsd11" }
123
124
  let(:ldd_output) { "ldd: illegal option -- -" }
124
125
 
125
126
  it "returns the darwin build" do
@@ -59,7 +59,7 @@ describe Appsignal::Transaction do
59
59
  new_transaction = create_transaction("2")
60
60
  expect(new_transaction).to eq(current_transaction)
61
61
  expect(new_transaction.transaction_id).to eq(transaction_id)
62
- end.to_not change { current_transaction }
62
+ end.to_not(change { current_transaction })
63
63
  end
64
64
 
65
65
  it "logs a debug message" do
@@ -835,7 +835,7 @@ describe Appsignal::Transaction do
835
835
  end
836
836
 
837
837
  context "with AppSignal filtering" do
838
- before { Appsignal.config.config_hash[:filter_parameters] = %w(foo) }
838
+ before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
839
839
  after { Appsignal.config.config_hash[:filter_parameters] = [] }
840
840
 
841
841
  it "returns sanitized custom params" do
@@ -871,16 +871,16 @@ describe Appsignal::Transaction do
871
871
 
872
872
  context "with an array" do
873
873
  let(:request) do
874
- Appsignal::Transaction::GenericRequest.new(background_env_with_data(:params => %w(arg1 arg2)))
874
+ Appsignal::Transaction::GenericRequest.new(background_env_with_data(:params => %w[arg1 arg2]))
875
875
  end
876
876
 
877
- it { is_expected.to eq %w(arg1 arg2) }
877
+ it { is_expected.to eq %w[arg1 arg2] }
878
878
 
879
879
  context "with AppSignal filtering" do
880
- before { Appsignal.config.config_hash[:filter_parameters] = %w(foo) }
880
+ before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
881
881
  after { Appsignal.config.config_hash[:filter_parameters] = [] }
882
882
 
883
- it { is_expected.to eq %w(arg1 arg2) }
883
+ it { is_expected.to eq %w[arg1 arg2] }
884
884
  end
885
885
  end
886
886
 
@@ -901,7 +901,7 @@ describe Appsignal::Transaction do
901
901
  Appsignal::Transaction::GenericRequest.new \
902
902
  http_request_env_with_data(:params => { :foo => :bar, :baz => :bat })
903
903
  end
904
- before { Appsignal.config.config_hash[:filter_parameters] = %w(foo) }
904
+ before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
905
905
  after { Appsignal.config.config_hash[:filter_parameters] = [] }
906
906
 
907
907
  it "should call the params sanitizer with filtering" do
@@ -1055,7 +1055,7 @@ describe Appsignal::Transaction do
1055
1055
  :both_symbols => :valid_value,
1056
1056
  :integer_value => 1,
1057
1057
  :hash_value => { "invalid" => "hash" },
1058
- :array_value => %w(invalid array),
1058
+ :array_value => %w[invalid array],
1059
1059
  :to_long_value => SecureRandom.urlsafe_base64(101),
1060
1060
  :object => Object.new,
1061
1061
  SecureRandom.urlsafe_base64(101) => "to_long_key"
@@ -82,7 +82,7 @@ describe Appsignal::Utils::ParamsSanitizer do
82
82
 
83
83
  context "with :filter_parameters option" do
84
84
  let(:sanitized_params) do
85
- described_class.sanitize(params, :filter_parameters => %w(text hash))
85
+ described_class.sanitize(params, :filter_parameters => %w[text hash])
86
86
  end
87
87
  subject { sanitized_params }
88
88
 
@@ -100,7 +100,7 @@ describe Appsignal::Utils::ParamsSanitizer do
100
100
 
101
101
  context "with strings as key filter values" do
102
102
  let(:sanitized_params) do
103
- described_class.sanitize(params, :filter_parameters => %w(string))
103
+ described_class.sanitize(params, :filter_parameters => %w[string])
104
104
  end
105
105
 
106
106
  it "sanitizes values" do
@@ -110,7 +110,7 @@ describe Appsignal::Utils::ParamsSanitizer do
110
110
 
111
111
  describe ":hash" do
112
112
  let(:sanitized_params) do
113
- described_class.sanitize(params, :filter_parameters => %w(nested_text))
113
+ described_class.sanitize(params, :filter_parameters => %w[nested_text])
114
114
  end
115
115
  subject { sanitized_params[:hash] }
116
116
 
@@ -121,7 +121,7 @@ describe Appsignal::Utils::ParamsSanitizer do
121
121
  describe ":nested_array" do
122
122
  describe ":nested_hash" do
123
123
  let(:sanitized_params) do
124
- described_class.sanitize(params, :filter_parameters => %w(key))
124
+ described_class.sanitize(params, :filter_parameters => %w[key])
125
125
  end
126
126
  subject { sanitized_params[:hash][:nested_array][3] }
127
127