appsignal 2.5.0.alpha.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +66 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.travis.yml +72 -0
  7. data/.yardopts +8 -0
  8. data/CHANGELOG.md +639 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +20 -0
  11. data/README.md +264 -0
  12. data/Rakefile +214 -0
  13. data/appsignal.gemspec +42 -0
  14. data/benchmark.rake +77 -0
  15. data/bin/appsignal +13 -0
  16. data/ext/Rakefile +27 -0
  17. data/ext/agent.yml +64 -0
  18. data/ext/appsignal_extension.c +692 -0
  19. data/ext/base.rb +79 -0
  20. data/ext/extconf.rb +35 -0
  21. data/gemfiles/capistrano2.gemfile +7 -0
  22. data/gemfiles/capistrano3.gemfile +7 -0
  23. data/gemfiles/grape.gemfile +7 -0
  24. data/gemfiles/no_dependencies.gemfile +5 -0
  25. data/gemfiles/padrino.gemfile +7 -0
  26. data/gemfiles/que.gemfile +5 -0
  27. data/gemfiles/rails-3.2.gemfile +6 -0
  28. data/gemfiles/rails-4.0.gemfile +6 -0
  29. data/gemfiles/rails-4.1.gemfile +6 -0
  30. data/gemfiles/rails-4.2.gemfile +10 -0
  31. data/gemfiles/rails-5.0.gemfile +5 -0
  32. data/gemfiles/rails-5.1.gemfile +5 -0
  33. data/gemfiles/resque.gemfile +12 -0
  34. data/gemfiles/sequel-435.gemfile +11 -0
  35. data/gemfiles/sequel.gemfile +11 -0
  36. data/gemfiles/sinatra.gemfile +6 -0
  37. data/gemfiles/webmachine.gemfile +5 -0
  38. data/lib/appsignal.rb +804 -0
  39. data/lib/appsignal/auth_check.rb +65 -0
  40. data/lib/appsignal/capistrano.rb +10 -0
  41. data/lib/appsignal/cli.rb +108 -0
  42. data/lib/appsignal/cli/demo.rb +63 -0
  43. data/lib/appsignal/cli/diagnose.rb +500 -0
  44. data/lib/appsignal/cli/helpers.rb +72 -0
  45. data/lib/appsignal/cli/install.rb +277 -0
  46. data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
  47. data/lib/appsignal/config.rb +287 -0
  48. data/lib/appsignal/demo.rb +107 -0
  49. data/lib/appsignal/event_formatter.rb +74 -0
  50. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
  51. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
  52. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
  53. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
  54. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
  55. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
  56. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  57. data/lib/appsignal/extension.rb +63 -0
  58. data/lib/appsignal/extension/jruby.rb +460 -0
  59. data/lib/appsignal/garbage_collection_profiler.rb +48 -0
  60. data/lib/appsignal/hooks.rb +105 -0
  61. data/lib/appsignal/hooks/action_cable.rb +113 -0
  62. data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
  63. data/lib/appsignal/hooks/celluloid.rb +30 -0
  64. data/lib/appsignal/hooks/data_mapper.rb +18 -0
  65. data/lib/appsignal/hooks/delayed_job.rb +19 -0
  66. data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
  67. data/lib/appsignal/hooks/net_http.rb +29 -0
  68. data/lib/appsignal/hooks/passenger.rb +22 -0
  69. data/lib/appsignal/hooks/puma.rb +35 -0
  70. data/lib/appsignal/hooks/que.rb +21 -0
  71. data/lib/appsignal/hooks/rake.rb +39 -0
  72. data/lib/appsignal/hooks/redis.rb +30 -0
  73. data/lib/appsignal/hooks/sequel.rb +60 -0
  74. data/lib/appsignal/hooks/shoryuken.rb +43 -0
  75. data/lib/appsignal/hooks/sidekiq.rb +144 -0
  76. data/lib/appsignal/hooks/unicorn.rb +40 -0
  77. data/lib/appsignal/hooks/webmachine.rb +23 -0
  78. data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
  79. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
  80. data/lib/appsignal/integrations/data_mapper.rb +33 -0
  81. data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
  82. data/lib/appsignal/integrations/grape.rb +53 -0
  83. data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
  84. data/lib/appsignal/integrations/object.rb +35 -0
  85. data/lib/appsignal/integrations/padrino.rb +84 -0
  86. data/lib/appsignal/integrations/que.rb +43 -0
  87. data/lib/appsignal/integrations/railtie.rb +41 -0
  88. data/lib/appsignal/integrations/rake.rb +2 -0
  89. data/lib/appsignal/integrations/resque.rb +20 -0
  90. data/lib/appsignal/integrations/resque_active_job.rb +30 -0
  91. data/lib/appsignal/integrations/sinatra.rb +17 -0
  92. data/lib/appsignal/integrations/webmachine.rb +38 -0
  93. data/lib/appsignal/js_exception_transaction.rb +54 -0
  94. data/lib/appsignal/marker.rb +63 -0
  95. data/lib/appsignal/minutely.rb +42 -0
  96. data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
  97. data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
  98. data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
  99. data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
  100. data/lib/appsignal/rack/streaming_listener.rb +73 -0
  101. data/lib/appsignal/system.rb +81 -0
  102. data/lib/appsignal/transaction.rb +498 -0
  103. data/lib/appsignal/transmitter.rb +107 -0
  104. data/lib/appsignal/utils.rb +127 -0
  105. data/lib/appsignal/utils/params_sanitizer.rb +59 -0
  106. data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
  107. data/lib/appsignal/version.rb +3 -0
  108. data/lib/sequel/extensions/appsignal_integration.rb +3 -0
  109. data/resources/appsignal.yml.erb +39 -0
  110. data/resources/cacert.pem +3866 -0
  111. data/spec/.rubocop.yml +7 -0
  112. data/spec/lib/appsignal/auth_check_spec.rb +80 -0
  113. data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
  114. data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
  115. data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
  116. data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
  117. data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
  118. data/spec/lib/appsignal/cli/install_spec.rb +632 -0
  119. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
  120. data/spec/lib/appsignal/cli_spec.rb +56 -0
  121. data/spec/lib/appsignal/config_spec.rb +637 -0
  122. data/spec/lib/appsignal/demo_spec.rb +87 -0
  123. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
  124. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
  125. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
  126. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
  127. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
  128. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
  129. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
  130. data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
  131. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  132. data/spec/lib/appsignal/extension_spec.rb +137 -0
  133. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
  134. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  135. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
  136. data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
  137. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
  138. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
  139. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
  140. data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
  141. data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
  142. data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
  143. data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
  144. data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
  145. data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
  146. data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
  147. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
  148. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
  149. data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
  150. data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
  151. data/spec/lib/appsignal/hooks_spec.rb +195 -0
  152. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
  153. data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
  154. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
  155. data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
  156. data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
  157. data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
  158. data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
  159. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
  160. data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
  161. data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
  162. data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
  163. data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
  164. data/spec/lib/appsignal/marker_spec.rb +51 -0
  165. data/spec/lib/appsignal/minutely_spec.rb +50 -0
  166. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
  167. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
  168. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
  169. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
  170. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
  171. data/spec/lib/appsignal/system_spec.rb +131 -0
  172. data/spec/lib/appsignal/transaction_spec.rb +1146 -0
  173. data/spec/lib/appsignal/transmitter_spec.rb +152 -0
  174. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
  175. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
  176. data/spec/lib/appsignal/utils_spec.rb +150 -0
  177. data/spec/lib/appsignal_spec.rb +1049 -0
  178. data/spec/spec_helper.rb +116 -0
  179. data/spec/support/fixtures/containers/cgroups/docker +14 -0
  180. data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
  181. data/spec/support/fixtures/containers/cgroups/lxc +10 -0
  182. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  183. data/spec/support/fixtures/containers/cgroups/none +1 -0
  184. data/spec/support/fixtures/generated_config.yml +24 -0
  185. data/spec/support/fixtures/uploaded_file.txt +0 -0
  186. data/spec/support/helpers/api_request_helper.rb +19 -0
  187. data/spec/support/helpers/cli_helpers.rb +26 -0
  188. data/spec/support/helpers/config_helpers.rb +21 -0
  189. data/spec/support/helpers/dependency_helper.rb +73 -0
  190. data/spec/support/helpers/directory_helper.rb +27 -0
  191. data/spec/support/helpers/env_helpers.rb +33 -0
  192. data/spec/support/helpers/example_exception.rb +13 -0
  193. data/spec/support/helpers/example_standard_error.rb +13 -0
  194. data/spec/support/helpers/log_helpers.rb +22 -0
  195. data/spec/support/helpers/std_streams_helper.rb +66 -0
  196. data/spec/support/helpers/system_helpers.rb +8 -0
  197. data/spec/support/helpers/time_helpers.rb +11 -0
  198. data/spec/support/helpers/transaction_helpers.rb +37 -0
  199. data/spec/support/matchers/contains_log.rb +7 -0
  200. data/spec/support/mocks/fake_gc_profiler.rb +19 -0
  201. data/spec/support/mocks/mock_extension.rb +6 -0
  202. data/spec/support/project_fixture/config/application.rb +0 -0
  203. data/spec/support/project_fixture/config/appsignal.yml +32 -0
  204. data/spec/support/project_fixture/config/environments/development.rb +0 -0
  205. data/spec/support/project_fixture/config/environments/production.rb +0 -0
  206. data/spec/support/project_fixture/config/environments/test.rb +0 -0
  207. data/spec/support/project_fixture/log/.gitkeep +0 -0
  208. data/spec/support/rails/my_app.rb +6 -0
  209. data/spec/support/shared_examples/instrument.rb +43 -0
  210. data/spec/support/stubs/delayed_job.rb +0 -0
  211. metadata +483 -0
@@ -0,0 +1,100 @@
1
+ class MockFormatter < Appsignal::EventFormatter
2
+ register "mock"
3
+
4
+ attr_reader :body
5
+
6
+ def initialize
7
+ @body = "some value"
8
+ end
9
+
10
+ def format(_payload)
11
+ ["title", body]
12
+ end
13
+ end
14
+
15
+ class MissingFormatMockFormatter < Appsignal::EventFormatter
16
+ def transform(_payload)
17
+ end
18
+ end
19
+
20
+ class IncorrectFormatMockFormatter < Appsignal::EventFormatter
21
+ def format
22
+ end
23
+ end
24
+
25
+ class MockDependentFormatter < Appsignal::EventFormatter
26
+ register "mock.dependent"
27
+
28
+ def initialize
29
+ NonsenseDependency.something
30
+ end
31
+ end
32
+
33
+ describe Appsignal::EventFormatter do
34
+ before do
35
+ Appsignal::EventFormatter.initialize_formatters
36
+ end
37
+
38
+ let(:klass) { Appsignal::EventFormatter }
39
+
40
+ context "registering and unregistering formatters" do
41
+ it "should register a formatter" do
42
+ expect(klass.formatters["mock"]).to be_instance_of(MockFormatter)
43
+ end
44
+
45
+ it "should know wether a formatter is registered" do
46
+ expect(klass.registered?("mock")).to be_truthy
47
+ expect(klass.registered?("mock", MockFormatter)).to be_truthy
48
+ expect(klass.registered?("mock", Hash)).to be_falsy
49
+ expect(klass.registered?("nonsense")).to be_falsy
50
+ end
51
+
52
+ it "doesn't register formatters that raise a name error in the initializer" do
53
+ expect(klass.registered?("mock.dependent")).to be_falsy
54
+ end
55
+
56
+ it "doesn't register formatters that don't have a format(payload) method" do
57
+ klass.register("mock.missing_format", MissingFormatMockFormatter)
58
+ klass.register("mock.incorrect_format", IncorrectFormatMockFormatter)
59
+
60
+ Appsignal::EventFormatter.initialize_formatters
61
+
62
+ expect(klass.registered?("mock.missing_format")).to be_falsy
63
+ expect(klass.registered?("mock.incorrect_format")).to be_falsy
64
+ end
65
+
66
+ it "should register a custom formatter" do
67
+ klass.register("mock.specific", MockFormatter)
68
+ Appsignal::EventFormatter.initialize_formatters
69
+
70
+ expect(klass.formatter_classes["mock.specific"]).to eq MockFormatter
71
+ expect(klass.registered?("mock.specific")).to be_truthy
72
+ expect(klass.formatters["mock.specific"]).to be_instance_of(MockFormatter)
73
+ expect(klass.formatters["mock.specific"].body).to eq "some value"
74
+ end
75
+
76
+ it "should not have a formatter that's not registered" do
77
+ expect(klass.formatters["nonsense"]).to be_nil
78
+ end
79
+
80
+ it "should unregister a formatter if the registered one has the same class" do
81
+ klass.register("mock.unregister", MockFormatter)
82
+
83
+ klass.unregister("mock.unregister", Hash)
84
+ expect(klass.registered?("mock.unregister")).to be_truthy
85
+
86
+ klass.unregister("mock.unregister", MockFormatter)
87
+ expect(klass.registered?("mock.unregister")).to be_falsy
88
+ end
89
+ end
90
+
91
+ context "calling formatters" do
92
+ it "should return nil if there is no formatter registered" do
93
+ expect(klass.format("nonsense", {})).to be_nil
94
+ end
95
+
96
+ it "should call the formatter if it is registered and use a value set in the initializer" do
97
+ expect(klass.format("mock", {})).to eq ["title", "some value"]
98
+ end
99
+ end
100
+ 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
@@ -0,0 +1,137 @@
1
+ describe Appsignal::Extension do
2
+ describe ".agent_config" do
3
+ subject { Appsignal::Extension.agent_config }
4
+
5
+ it { is_expected.to have_key("version") }
6
+ it { is_expected.to have_key("triples") }
7
+ end
8
+
9
+ describe ".agent_version" do
10
+ subject { Appsignal::Extension.agent_version }
11
+
12
+ it { is_expected.to be_kind_of(String) }
13
+ end
14
+
15
+ context "when the extension library can be loaded" do
16
+ subject { Appsignal::Extension }
17
+
18
+ it "should indicate that the extension is loaded" do
19
+ expect(Appsignal.extension_loaded?).to be_truthy
20
+ end
21
+
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
45
+ end
46
+
47
+ context "with a valid config" do
48
+ before do
49
+ project_fixture_config.write_to_environment
50
+ end
51
+
52
+ it "should have a start and stop method" do
53
+ subject.start
54
+ subject.stop
55
+ end
56
+
57
+ context "with a transaction" do
58
+ subject { Appsignal::Extension.start_transaction("request_id", "http_request", 0) }
59
+
60
+ it "should have a start_event method" do
61
+ subject.start_event(0)
62
+ end
63
+
64
+ it "should have a finish_event method" do
65
+ subject.finish_event("name", "title", "body", 0, 0)
66
+ end
67
+
68
+ it "should have a record_event method" do
69
+ subject.record_event("name", "title", "body", 0, 1000, 1000)
70
+ end
71
+
72
+ it "should have a set_error method" do
73
+ subject.set_error("name", "message", Appsignal::Extension.data_map_new)
74
+ end
75
+
76
+ it "should have a set_sample_data method" do
77
+ subject.set_sample_data("params", Appsignal::Extension.data_map_new)
78
+ end
79
+
80
+ it "should have a set_action method" do
81
+ subject.set_action("value")
82
+ end
83
+
84
+ it "should have a set_namespace method" do
85
+ subject.set_namespace("value")
86
+ end
87
+
88
+ it "should have a set_queue_start method" do
89
+ subject.set_queue_start(10)
90
+ end
91
+
92
+ it "should have a set_metadata method" do
93
+ subject.set_metadata("key", "value")
94
+ end
95
+
96
+ it "should have a finish method" do
97
+ subject.finish(0)
98
+ end
99
+
100
+ it "should have a complete method" do
101
+ subject.complete
102
+ end
103
+ end
104
+
105
+ it "should have a set_gauge method" do
106
+ subject.set_gauge("key", 1.0)
107
+ end
108
+
109
+ it "should have a increment_counter method" do
110
+ subject.increment_counter("key", 1)
111
+ end
112
+
113
+ it "should have a add_distribution_value method" do
114
+ subject.add_distribution_value("key", 1.0)
115
+ end
116
+ end
117
+ end
118
+
119
+ context "when the extension library cannot be loaded" do
120
+ subject { Appsignal::Extension }
121
+
122
+ before do
123
+ allow(Appsignal).to receive(:extension_loaded).and_return(false)
124
+ allow(Appsignal).to receive(:testing?).and_return(false)
125
+ end
126
+
127
+ it "should indicate that the extension is not loaded" do
128
+ expect(Appsignal.extension_loaded?).to be_falsy
129
+ end
130
+
131
+ it "should not raise errors when methods are called" do
132
+ expect do
133
+ subject.something
134
+ end.not_to raise_error
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,66 @@
1
+ describe Appsignal::GarbageCollectionProfiler do
2
+ let(:internal_profiler) { FakeGCProfiler.new }
3
+ let(:profiler) { described_class.new }
4
+
5
+ before do
6
+ allow_any_instance_of(described_class)
7
+ .to receive(:internal_profiler)
8
+ .and_return(internal_profiler)
9
+ end
10
+
11
+ context "on initialization" do
12
+ it "has a total time of 0" do
13
+ expect(profiler.total_time).to eq(0)
14
+ end
15
+ end
16
+
17
+ context "when the GC has run" do
18
+ before { internal_profiler.total_time = 0.12345 }
19
+
20
+ it "fetches the total time from Ruby's GC::Profiler" do
21
+ expect(profiler.total_time).to eq(123)
22
+ end
23
+
24
+ it "clears Ruby's GC::Profiler afterward" do
25
+ expect(internal_profiler).to receive(:clear)
26
+ profiler.total_time
27
+ end
28
+ end
29
+
30
+ context "when the total GC time becomes too high" do
31
+ it "resets the total time" do
32
+ internal_profiler.total_time = 2_147_483_647
33
+ expect(profiler.total_time).to eq(0)
34
+ end
35
+ end
36
+
37
+ context "when the GC has run multiple times" do
38
+ it "adds all times from Ruby's GC::Profiler together" do
39
+ 2.times do
40
+ internal_profiler.total_time = 0.12345
41
+ profiler.total_time
42
+ end
43
+
44
+ expect(profiler.total_time).to eq(246)
45
+ end
46
+ end
47
+
48
+ context "when in multiple threads and with a slow GC::Profiler" do
49
+ it "does not count garbage collection times twice" do
50
+ threads = []
51
+ results = []
52
+ internal_profiler.clear_delay = 0.001
53
+ internal_profiler.total_time = 0.12345
54
+
55
+ 2.times do
56
+ threads << Thread.new do
57
+ profiler = Appsignal::GarbageCollectionProfiler.new
58
+ results << profiler.total_time
59
+ end
60
+ end
61
+
62
+ threads.each(&:join)
63
+ expect(results).to eq([123, 0])
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,370 @@
1
+ describe Appsignal::Hooks::ActionCableHook do
2
+ if DependencyHelper.action_cable_present?
3
+ context "with ActionCable" do
4
+ require "action_cable/engine"
5
+
6
+ describe ".dependencies_present?" do
7
+ subject { described_class.new.dependencies_present? }
8
+
9
+ it "returns true" do
10
+ is_expected.to be_truthy
11
+ end
12
+ end
13
+
14
+ describe ActionCable::Channel::Base do
15
+ let(:transaction) do
16
+ Appsignal::Transaction.new(
17
+ transaction_id,
18
+ Appsignal::Transaction::ACTION_CABLE,
19
+ ActionDispatch::Request.new(env)
20
+ )
21
+ end
22
+ let(:channel) do
23
+ Class.new(ActionCable::Channel::Base) do
24
+ def speak(_data)
25
+ end
26
+
27
+ def self.to_s
28
+ "MyChannel"
29
+ end
30
+ end
31
+ end
32
+ let(:log) { StringIO.new }
33
+ let(:server) do
34
+ ActionCable::Server::Base.new.tap do |s|
35
+ s.config.logger = ActiveSupport::Logger.new(log)
36
+ end
37
+ end
38
+ let(:connection) { ActionCable::Connection::Base.new(server, env) }
39
+ let(:identifier) { { :channel => "MyChannel" }.to_json }
40
+ let(:params) { {} }
41
+ let(:request_id) { SecureRandom.uuid }
42
+ let(:transaction_id) { request_id }
43
+ let(:env) do
44
+ http_request_env_with_data("action_dispatch.request_id" => request_id, :params => params)
45
+ end
46
+ let(:instance) { channel.new(connection, identifier, params) }
47
+ subject { transaction.to_h }
48
+ before do
49
+ start_agent
50
+ expect(Appsignal.active?).to be_truthy
51
+ transaction
52
+
53
+ expect(Appsignal::Transaction).to receive(:create)
54
+ .with(transaction_id, Appsignal::Transaction::ACTION_CABLE, kind_of(ActionDispatch::Request))
55
+ .and_return(transaction)
56
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
57
+ # Make sure sample data is added
58
+ expect(transaction.ext).to receive(:finish).and_return(true)
59
+ # Stub complete call, stops it from being cleared in the extension
60
+ # And allows us to call `#to_h` on it after it's been completed.
61
+ expect(transaction.ext).to receive(:complete)
62
+
63
+ # Stub transmit call for subscribe/unsubscribe tests
64
+ allow(connection).to receive(:websocket)
65
+ .and_return(instance_double("ActionCable::Connection::WebSocket", :transmit => nil))
66
+ end
67
+
68
+ describe "#perform_action" do
69
+ it "creates a transaction for an action" do
70
+ instance.perform_action("message" => "foo", "action" => "speak")
71
+
72
+ expect(subject).to include(
73
+ "action" => "MyChannel#speak",
74
+ "error" => nil,
75
+ "id" => transaction_id,
76
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
77
+ "metadata" => {
78
+ "method" => "websocket",
79
+ "path" => "/blog"
80
+ }
81
+ )
82
+ expect(subject["events"].first).to include(
83
+ "allocation_count" => kind_of(Integer),
84
+ "body" => "",
85
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
86
+ "child_allocation_count" => kind_of(Integer),
87
+ "child_duration" => kind_of(Float),
88
+ "child_gc_duration" => kind_of(Float),
89
+ "count" => 1,
90
+ "gc_duration" => kind_of(Float),
91
+ "start" => kind_of(Float),
92
+ "duration" => kind_of(Float),
93
+ "name" => "perform_action.action_cable",
94
+ "title" => ""
95
+ )
96
+ expect(subject["sample_data"]).to include(
97
+ "params" => {
98
+ "action" => "speak",
99
+ "message" => "foo"
100
+ }
101
+ )
102
+ end
103
+
104
+ context "without request_id (standalone server)" do
105
+ let(:request_id) { nil }
106
+ let(:transaction_id) { SecureRandom.uuid }
107
+ let(:action_transaction) do
108
+ Appsignal::Transaction.new(
109
+ transaction_id,
110
+ Appsignal::Transaction::ACTION_CABLE,
111
+ ActionDispatch::Request.new(env)
112
+ )
113
+ end
114
+ before do
115
+ # Stub future (private AppSignal) transaction id generated by the hook.
116
+ expect(SecureRandom).to receive(:uuid).and_return(transaction_id)
117
+ end
118
+
119
+ it "uses its own internal request_id set by the subscribed callback" do
120
+ # Subscribe action, sets the request_id
121
+ instance.subscribe_to_channel
122
+ expect(transaction.to_h["id"]).to eq(transaction_id)
123
+
124
+ # Expect another transaction for the action.
125
+ # This transaction will use the same request_id as the
126
+ # transaction id used to subscribe to the channel.
127
+ expect(Appsignal::Transaction).to receive(:create).with(
128
+ transaction_id,
129
+ Appsignal::Transaction::ACTION_CABLE,
130
+ kind_of(ActionDispatch::Request)
131
+ ).and_return(action_transaction)
132
+ allow(Appsignal::Transaction).to receive(:current).and_return(action_transaction)
133
+ # Stub complete call, stops it from being cleared in the extension
134
+ # And allows us to call `#to_h` on it after it's been completed.
135
+ expect(action_transaction.ext).to receive(:complete)
136
+
137
+ instance.perform_action("message" => "foo", "action" => "speak")
138
+ expect(action_transaction.to_h["id"]).to eq(transaction_id)
139
+ end
140
+ end
141
+
142
+ context "with an error in the action" do
143
+ let(:channel) do
144
+ Class.new(ActionCable::Channel::Base) do
145
+ def speak(_data)
146
+ raise ExampleException, "oh no!"
147
+ end
148
+
149
+ def self.to_s
150
+ "MyChannel"
151
+ end
152
+ end
153
+ end
154
+
155
+ it "registers an error on the transaction" do
156
+ expect do
157
+ instance.perform_action("message" => "foo", "action" => "speak")
158
+ end.to raise_error(ExampleException)
159
+
160
+ expect(subject).to include(
161
+ "action" => "MyChannel#speak",
162
+ "id" => transaction_id,
163
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
164
+ "metadata" => {
165
+ "method" => "websocket",
166
+ "path" => "/blog"
167
+ }
168
+ )
169
+ expect(subject["error"]).to include(
170
+ "backtrace" => kind_of(String),
171
+ "name" => "ExampleException",
172
+ "message" => "oh no!"
173
+ )
174
+ expect(subject["sample_data"]).to include(
175
+ "params" => {
176
+ "action" => "speak",
177
+ "message" => "foo"
178
+ }
179
+ )
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "subscribe callback" do
185
+ let(:params) { { "internal" => true } }
186
+
187
+ it "creates a transaction for a subscription" do
188
+ instance.subscribe_to_channel
189
+
190
+ expect(subject).to include(
191
+ "action" => "MyChannel#subscribed",
192
+ "error" => nil,
193
+ "id" => transaction_id,
194
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
195
+ "metadata" => {
196
+ "method" => "websocket",
197
+ "path" => "/blog"
198
+ }
199
+ )
200
+ expect(subject["events"].first).to include(
201
+ "allocation_count" => kind_of(Integer),
202
+ "body" => "",
203
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
204
+ "child_allocation_count" => kind_of(Integer),
205
+ "child_duration" => kind_of(Float),
206
+ "child_gc_duration" => kind_of(Float),
207
+ "count" => 1,
208
+ "gc_duration" => kind_of(Float),
209
+ "start" => kind_of(Float),
210
+ "duration" => kind_of(Float),
211
+ "name" => "subscribed.action_cable",
212
+ "title" => ""
213
+ )
214
+ expect(subject["sample_data"]).to include(
215
+ "params" => { "internal" => "true" }
216
+ )
217
+ end
218
+
219
+ context "without request_id (standalone server)" do
220
+ let(:request_id) { nil }
221
+ let(:transaction_id) { SecureRandom.uuid }
222
+ before do
223
+ allow(SecureRandom).to receive(:uuid).and_return(transaction_id)
224
+ instance.subscribe_to_channel
225
+ end
226
+
227
+ it "uses its own internal request_id" do
228
+ expect(subject["id"]).to eq(transaction_id)
229
+ end
230
+ end
231
+
232
+ context "with an error in the callback" do
233
+ let(:channel) do
234
+ Class.new(ActionCable::Channel::Base) do
235
+ def subscribed
236
+ raise ExampleException, "oh no!"
237
+ end
238
+
239
+ def self.to_s
240
+ "MyChannel"
241
+ end
242
+ end
243
+ end
244
+
245
+ it "registers an error on the transaction" do
246
+ expect do
247
+ instance.subscribe_to_channel
248
+ end.to raise_error(ExampleException)
249
+
250
+ expect(subject).to include(
251
+ "action" => "MyChannel#subscribed",
252
+ "id" => transaction_id,
253
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
254
+ "metadata" => {
255
+ "method" => "websocket",
256
+ "path" => "/blog"
257
+ }
258
+ )
259
+ expect(subject["error"]).to include(
260
+ "backtrace" => kind_of(String),
261
+ "name" => "ExampleException",
262
+ "message" => "oh no!"
263
+ )
264
+ expect(subject["sample_data"]).to include(
265
+ "params" => { "internal" => "true" }
266
+ )
267
+ end
268
+ end
269
+ end
270
+
271
+ describe "unsubscribe callback" do
272
+ let(:params) { { "internal" => true } }
273
+
274
+ it "creates a transaction for a subscription" do
275
+ instance.unsubscribe_from_channel
276
+
277
+ expect(subject).to include(
278
+ "action" => "MyChannel#unsubscribed",
279
+ "error" => nil,
280
+ "id" => transaction_id,
281
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
282
+ "metadata" => {
283
+ "method" => "websocket",
284
+ "path" => "/blog"
285
+ }
286
+ )
287
+ expect(subject["events"].first).to include(
288
+ "allocation_count" => kind_of(Integer),
289
+ "body" => "",
290
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
291
+ "child_allocation_count" => kind_of(Integer),
292
+ "child_duration" => kind_of(Float),
293
+ "child_gc_duration" => kind_of(Float),
294
+ "count" => 1,
295
+ "gc_duration" => kind_of(Float),
296
+ "start" => kind_of(Float),
297
+ "duration" => kind_of(Float),
298
+ "name" => "unsubscribed.action_cable",
299
+ "title" => ""
300
+ )
301
+ expect(subject["sample_data"]).to include(
302
+ "params" => { "internal" => "true" }
303
+ )
304
+ end
305
+
306
+ context "without request_id (standalone server)" do
307
+ let(:request_id) { nil }
308
+ let(:transaction_id) { SecureRandom.uuid }
309
+ before do
310
+ allow(SecureRandom).to receive(:uuid).and_return(transaction_id)
311
+ instance.unsubscribe_from_channel
312
+ end
313
+
314
+ it "uses its own internal request_id" do
315
+ expect(subject["id"]).to eq(transaction_id)
316
+ end
317
+ end
318
+
319
+ context "with an error in the callback" do
320
+ let(:channel) do
321
+ Class.new(ActionCable::Channel::Base) do
322
+ def unsubscribed
323
+ raise ExampleException, "oh no!"
324
+ end
325
+
326
+ def self.to_s
327
+ "MyChannel"
328
+ end
329
+ end
330
+ end
331
+
332
+ it "registers an error on the transaction" do
333
+ expect do
334
+ instance.unsubscribe_from_channel
335
+ end.to raise_error(ExampleException)
336
+
337
+ expect(subject).to include(
338
+ "action" => "MyChannel#unsubscribed",
339
+ "id" => transaction_id,
340
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
341
+ "metadata" => {
342
+ "method" => "websocket",
343
+ "path" => "/blog"
344
+ }
345
+ )
346
+ expect(subject["error"]).to include(
347
+ "backtrace" => kind_of(String),
348
+ "name" => "ExampleException",
349
+ "message" => "oh no!"
350
+ )
351
+ expect(subject["sample_data"]).to include(
352
+ "params" => { "internal" => "true" }
353
+ )
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
359
+ else
360
+ context "without ActionCable" do
361
+ describe ".dependencies_present?" do
362
+ subject { described_class.new.dependencies_present? }
363
+
364
+ it "returns false" do
365
+ is_expected.to be_falsy
366
+ end
367
+ end
368
+ end
369
+ end
370
+ end