appsignal 2.11.0.beta.2-java → 2.11.1.beta.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +57 -1
  3. data/CHANGELOG.md +28 -0
  4. data/README.md +11 -5
  5. data/Rakefile +27 -9
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +2 -2
  8. data/ext/Rakefile +2 -0
  9. data/ext/agent.yml +17 -25
  10. data/ext/appsignal_extension.c +1 -1
  11. data/ext/base.rb +7 -0
  12. data/ext/extconf.rb +2 -0
  13. data/lib/appsignal.rb +1 -0
  14. data/lib/appsignal/auth_check.rb +4 -2
  15. data/lib/appsignal/cli/diagnose.rb +1 -1
  16. data/lib/appsignal/config.rb +82 -17
  17. data/lib/appsignal/extension.rb +6 -5
  18. data/lib/appsignal/extension/jruby.rb +6 -5
  19. data/lib/appsignal/hooks.rb +24 -0
  20. data/lib/appsignal/hooks/action_mailer.rb +22 -0
  21. data/lib/appsignal/hooks/active_job.rb +53 -5
  22. data/lib/appsignal/hooks/active_support_notifications.rb +72 -0
  23. data/lib/appsignal/hooks/puma.rb +0 -1
  24. data/lib/appsignal/hooks/sidekiq.rb +1 -2
  25. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
  26. data/lib/appsignal/probes.rb +7 -0
  27. data/lib/appsignal/probes/puma.rb +1 -1
  28. data/lib/appsignal/probes/sidekiq.rb +3 -1
  29. data/lib/appsignal/utils/deprecation_message.rb +1 -1
  30. data/lib/appsignal/version.rb +1 -1
  31. data/spec/lib/appsignal/auth_check_spec.rb +23 -0
  32. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  33. data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
  34. data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
  35. data/spec/lib/appsignal/config_spec.rb +39 -1
  36. data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
  37. data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
  38. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  39. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  40. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  41. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  42. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  43. data/spec/lib/appsignal/hooks/activejob_spec.rb +143 -10
  44. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +3 -14
  45. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +7 -5
  46. data/spec/lib/appsignal/hooks_spec.rb +57 -0
  47. data/spec/lib/appsignal/marker_spec.rb +1 -1
  48. data/spec/spec_helper.rb +5 -0
  49. data/spec/support/helpers/config_helpers.rb +3 -2
  50. data/spec/support/helpers/dependency_helper.rb +4 -0
  51. data/spec/support/helpers/transaction_helpers.rb +1 -1
  52. data/spec/support/testing.rb +19 -19
  53. metadata +19 -7
@@ -1,4 +1,18 @@
1
1
  describe Appsignal::Config do
2
+ describe "config keys" do
3
+ it "all config keys have an environment variable version registered" do
4
+ config = Appsignal::Config
5
+ mapped_env_keys = config::ENV_TO_KEY_MAPPING.keys.sort
6
+ configured_env_keys = (
7
+ config::ENV_STRING_KEYS +
8
+ config::ENV_BOOLEAN_KEYS +
9
+ config::ENV_ARRAY_KEYS
10
+ ).sort
11
+
12
+ expect(mapped_env_keys).to eql(configured_env_keys)
13
+ end
14
+ end
15
+
2
16
  describe "#initialize" do
3
17
  describe "environment" do
4
18
  context "when environment is nil" do
@@ -244,6 +258,27 @@ describe Appsignal::Config do
244
258
  end
245
259
  end
246
260
 
261
+ context "with an overriden config file" do
262
+ let(:config) do
263
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "appsignal.yml"))
264
+ end
265
+
266
+ it "is valid and active" do
267
+ expect(config.valid?).to be_truthy
268
+ expect(config.active?).to be_truthy
269
+ end
270
+
271
+ context "with an invalid overriden config file" do
272
+ let(:config) do
273
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "missing.yml"))
274
+ end
275
+
276
+ it "is not valid" do
277
+ expect(config.valid?).to be_falsy
278
+ end
279
+ end
280
+ end
281
+
247
282
  context "with the config file causing an error" do
248
283
  let(:config_path) do
249
284
  File.expand_path(
@@ -397,6 +432,7 @@ describe Appsignal::Config do
397
432
  :debug => true
398
433
  )
399
434
  end
435
+ let(:working_directory_path) { File.join(tmp_dir, "test_working_directory_path") }
400
436
  let(:env_config) do
401
437
  {
402
438
  :running_in_container => true,
@@ -413,7 +449,8 @@ describe Appsignal::Config do
413
449
  :files_world_accessible => false,
414
450
  :request_headers => %w[accept accept-charset],
415
451
  :revision => "v2.5.1",
416
- :send_environment_metadata => false
452
+ :send_environment_metadata => false,
453
+ :working_directory_path => working_directory_path
417
454
  }
418
455
  end
419
456
  before do
@@ -431,6 +468,7 @@ describe Appsignal::Config do
431
468
  ENV["APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = "false"
432
469
  ENV["APPSIGNAL_REQUEST_HEADERS"] = "accept,accept-charset"
433
470
  ENV["APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = "false"
471
+ ENV["APPSIGNAL_WORKING_DIRECTORY_PATH"] = working_directory_path
434
472
  ENV["APP_REVISION"] = "v2.5.1"
435
473
  end
436
474
 
@@ -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
@@ -0,0 +1,54 @@
1
+ describe Appsignal::Hooks::ActionMailerHook do
2
+ if DependencyHelper.action_mailer_present? &&
3
+ DependencyHelper.rails_version >= Gem::Version.new("4.0.0")
4
+ context "with ActionMailer" do
5
+ require "action_mailer"
6
+
7
+ class UserMailer < ActionMailer::Base
8
+ default :from => "test@example.com"
9
+
10
+ def welcome
11
+ mail(:to => "test@example.com", :subject => "ActionMailer test", :content_type => "text/html") do |format|
12
+ format.html { render :html => "This is a test" }
13
+ end
14
+ end
15
+ end
16
+ UserMailer.delivery_method = :test
17
+
18
+ describe ".dependencies_present?" do
19
+ subject { described_class.new.dependencies_present? }
20
+
21
+ it "returns true" do
22
+ is_expected.to be_truthy
23
+ end
24
+ end
25
+
26
+ describe ".install" do
27
+ before do
28
+ start_agent
29
+ expect(Appsignal.active?).to be_truthy
30
+ end
31
+
32
+ it "is subscribed to 'process.action_mailer' and processes instrumentation" do
33
+ expect(Appsignal).to receive(:increment_counter).with(
34
+ :action_mailer_process,
35
+ 1,
36
+ :mailer => "UserMailer", :action => :welcome
37
+ )
38
+
39
+ UserMailer.welcome.deliver_now
40
+ end
41
+ end
42
+ end
43
+ else
44
+ context "without ActionMailer" do
45
+ describe ".dependencies_present?" do
46
+ subject { described_class.new.dependencies_present? }
47
+
48
+ it "returns false" do
49
+ is_expected.to be_falsy
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ shared_examples "activesupport finish_with_state override" do
2
+ let(:instrumenter) { as.instrumenter }
3
+
4
+ it "instruments an ActiveSupport::Notifications.start/finish event with payload on finish" do
5
+ listeners_state = instrumenter.start("sql.active_record", {})
6
+ instrumenter.finish_with_state(listeners_state, "sql.active_record", :sql => "SQL")
7
+
8
+ expect(transaction.to_h["events"]).to match([
9
+ {
10
+ "allocation_count" => kind_of(Integer),
11
+ "body" => "SQL",
12
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
13
+ "child_allocation_count" => kind_of(Integer),
14
+ "child_duration" => kind_of(Float),
15
+ "child_gc_duration" => kind_of(Float),
16
+ "count" => 1,
17
+ "duration" => kind_of(Float),
18
+ "gc_duration" => kind_of(Float),
19
+ "name" => "sql.active_record",
20
+ "start" => kind_of(Float),
21
+ "title" => ""
22
+ }
23
+ ])
24
+ end
25
+
26
+ it "does not instrument events whose name starts with a bang" do
27
+ expect(Appsignal::Transaction.current).not_to receive(:start_event)
28
+ expect(Appsignal::Transaction.current).not_to receive(:finish_event)
29
+
30
+ listeners_state = instrumenter.start("!sql.active_record", {})
31
+ instrumenter.finish_with_state(listeners_state, "!sql.active_record", :sql => "SQL")
32
+
33
+ expect(transaction.to_h["events"]).to be_empty
34
+ end
35
+ end
@@ -0,0 +1,145 @@
1
+ shared_examples "activesupport instrument override" do
2
+ it "instruments an ActiveSupport::Notifications.instrument event" do
3
+ return_value = as.instrument("sql.active_record", :sql => "SQL") do
4
+ "value"
5
+ end
6
+
7
+ expect(return_value).to eq "value"
8
+ expect(transaction.to_h["events"]).to match([
9
+ {
10
+ "allocation_count" => kind_of(Integer),
11
+ "body" => "SQL",
12
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
13
+ "child_allocation_count" => kind_of(Integer),
14
+ "child_duration" => kind_of(Float),
15
+ "child_gc_duration" => kind_of(Float),
16
+ "count" => 1,
17
+ "duration" => kind_of(Float),
18
+ "gc_duration" => kind_of(Float),
19
+ "name" => "sql.active_record",
20
+ "start" => kind_of(Float),
21
+ "title" => ""
22
+ }
23
+ ])
24
+ end
25
+
26
+ it "instruments an ActiveSupport::Notifications.instrument event with no registered formatter" do
27
+ return_value = as.instrument("no-registered.formatter", :key => "something") do
28
+ "value"
29
+ end
30
+
31
+ expect(return_value).to eq "value"
32
+ expect(transaction.to_h["events"]).to match([
33
+ {
34
+ "allocation_count" => kind_of(Integer),
35
+ "body" => "",
36
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
37
+ "child_allocation_count" => kind_of(Integer),
38
+ "child_duration" => kind_of(Float),
39
+ "child_gc_duration" => kind_of(Float),
40
+ "count" => 1,
41
+ "duration" => kind_of(Float),
42
+ "gc_duration" => kind_of(Float),
43
+ "name" => "no-registered.formatter",
44
+ "start" => kind_of(Float),
45
+ "title" => ""
46
+ }
47
+ ])
48
+ end
49
+
50
+ it "converts non-string names to strings" do
51
+ as.instrument(:not_a_string) {}
52
+ expect(transaction.to_h["events"]).to match([
53
+ {
54
+ "allocation_count" => kind_of(Integer),
55
+ "body" => "",
56
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
57
+ "child_allocation_count" => kind_of(Integer),
58
+ "child_duration" => kind_of(Float),
59
+ "child_gc_duration" => kind_of(Float),
60
+ "count" => 1,
61
+ "duration" => kind_of(Float),
62
+ "gc_duration" => kind_of(Float),
63
+ "name" => "not_a_string",
64
+ "start" => kind_of(Float),
65
+ "title" => ""
66
+ }
67
+ ])
68
+ end
69
+
70
+ it "does not instrument events whose name starts with a bang" do
71
+ expect(Appsignal::Transaction.current).not_to receive(:start_event)
72
+ expect(Appsignal::Transaction.current).not_to receive(:finish_event)
73
+
74
+ return_value = as.instrument("!sql.active_record", :sql => "SQL") do
75
+ "value"
76
+ end
77
+
78
+ expect(return_value).to eq "value"
79
+ end
80
+
81
+ context "when an error is raised in an instrumented block" do
82
+ it "instruments an ActiveSupport::Notifications.instrument event" do
83
+ expect do
84
+ as.instrument("sql.active_record", :sql => "SQL") do
85
+ raise ExampleException, "foo"
86
+ end
87
+ end.to raise_error(ExampleException, "foo")
88
+
89
+ expect(transaction.to_h["events"]).to match([
90
+ {
91
+ "allocation_count" => kind_of(Integer),
92
+ "body" => "SQL",
93
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
94
+ "child_allocation_count" => kind_of(Integer),
95
+ "child_duration" => kind_of(Float),
96
+ "child_gc_duration" => kind_of(Float),
97
+ "count" => 1,
98
+ "duration" => kind_of(Float),
99
+ "gc_duration" => kind_of(Float),
100
+ "name" => "sql.active_record",
101
+ "start" => kind_of(Float),
102
+ "title" => ""
103
+ }
104
+ ])
105
+ end
106
+ end
107
+
108
+ context "when a message is thrown in an instrumented block" do
109
+ it "instruments an ActiveSupport::Notifications.instrument event" do
110
+ expect do
111
+ as.instrument("sql.active_record", :sql => "SQL") do
112
+ throw :foo
113
+ end
114
+ end.to throw_symbol(:foo)
115
+
116
+ expect(transaction.to_h["events"]).to match([
117
+ {
118
+ "allocation_count" => kind_of(Integer),
119
+ "body" => "SQL",
120
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
121
+ "child_allocation_count" => kind_of(Integer),
122
+ "child_duration" => kind_of(Float),
123
+ "child_gc_duration" => kind_of(Float),
124
+ "count" => 1,
125
+ "duration" => kind_of(Float),
126
+ "gc_duration" => kind_of(Float),
127
+ "name" => "sql.active_record",
128
+ "start" => kind_of(Float),
129
+ "title" => ""
130
+ }
131
+ ])
132
+ end
133
+ end
134
+
135
+ context "when a transaction is completed in an instrumented block" do
136
+ it "does not complete the ActiveSupport::Notifications.instrument event" do
137
+ expect(transaction).to receive(:complete)
138
+ as.instrument("sql.active_record", :sql => "SQL") do
139
+ Appsignal::Transaction.complete_current!
140
+ end
141
+
142
+ expect(transaction.to_h["events"]).to match([])
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,69 @@
1
+ shared_examples "activesupport start finish override" do
2
+ let(:instrumenter) { as.instrumenter }
3
+
4
+ it "instruments an ActiveSupport::Notifications.start/finish event with payload on start ignores payload" do
5
+ instrumenter.start("sql.active_record", :sql => "SQL")
6
+ instrumenter.finish("sql.active_record", {})
7
+
8
+ expect(transaction.to_h["events"]).to match([
9
+ {
10
+ "allocation_count" => kind_of(Integer),
11
+ "body" => "",
12
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
13
+ "child_allocation_count" => kind_of(Integer),
14
+ "child_duration" => kind_of(Float),
15
+ "child_gc_duration" => kind_of(Float),
16
+ "count" => 1,
17
+ "duration" => kind_of(Float),
18
+ "gc_duration" => kind_of(Float),
19
+ "name" => "sql.active_record",
20
+ "start" => kind_of(Float),
21
+ "title" => ""
22
+ }
23
+ ])
24
+ end
25
+
26
+ it "instruments an ActiveSupport::Notifications.start/finish event with payload on finish" do
27
+ instrumenter.start("sql.active_record", {})
28
+ instrumenter.finish("sql.active_record", :sql => "SQL")
29
+
30
+ expect(transaction.to_h["events"]).to match([
31
+ {
32
+ "allocation_count" => kind_of(Integer),
33
+ "body" => "SQL",
34
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
35
+ "child_allocation_count" => kind_of(Integer),
36
+ "child_duration" => kind_of(Float),
37
+ "child_gc_duration" => kind_of(Float),
38
+ "count" => 1,
39
+ "duration" => kind_of(Float),
40
+ "gc_duration" => kind_of(Float),
41
+ "name" => "sql.active_record",
42
+ "start" => kind_of(Float),
43
+ "title" => ""
44
+ }
45
+ ])
46
+ end
47
+
48
+ it "does not instrument events whose name starts with a bang" do
49
+ expect(Appsignal::Transaction.current).not_to receive(:start_event)
50
+ expect(Appsignal::Transaction.current).not_to receive(:finish_event)
51
+
52
+ instrumenter.start("!sql.active_record", {})
53
+ instrumenter.finish("!sql.active_record", {})
54
+
55
+ expect(transaction.to_h["events"]).to be_empty
56
+ end
57
+
58
+ context "when a transaction is completed in an instrumented block" do
59
+ it "does not complete the ActiveSupport::Notifications.instrument event" do
60
+ expect(transaction).to receive(:complete)
61
+
62
+ instrumenter.start("sql.active_record", {})
63
+ Appsignal::Transaction.complete_current!
64
+ instrumenter.finish("sql.active_record", {})
65
+
66
+ expect(transaction.to_h["events"]).to match([])
67
+ end
68
+ end
69
+ end