appsignal 2.5.0.alpha.1-java

Sign up to get free protection for your applications and to get access to all the features.
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,127 @@
1
+ require "appsignal/integrations/mongo_ruby_driver"
2
+ describe Appsignal::Hooks::MongoMonitorSubscriber do
3
+ let(:subscriber) { Appsignal::Hooks::MongoMonitorSubscriber.new }
4
+
5
+ context "with transaction" do
6
+ let!(:transaction) do
7
+ Appsignal::Transaction.create("1", "http_request", {}, {})
8
+ end
9
+
10
+ describe "#started" do
11
+ let(:event) do
12
+ double(
13
+ :request_id => 1,
14
+ :command_name => "find",
15
+ :command => { "foo" => "bar" }
16
+ )
17
+ end
18
+
19
+ it "should sanitize command" do
20
+ expect(Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter)
21
+ .to receive(:format).with("find", "foo" => "bar")
22
+
23
+ subscriber.started(event)
24
+ end
25
+
26
+ it "should store command on the transaction" do
27
+ subscriber.started(event)
28
+
29
+ expect(transaction.store("mongo_driver")).to eq(1 => { "foo" => "?" })
30
+ end
31
+
32
+ it "should start an event in the extension" do
33
+ expect(transaction).to receive(:start_event)
34
+
35
+ subscriber.started(event)
36
+ end
37
+ end
38
+
39
+ describe "#succeeded" do
40
+ let(:event) { double }
41
+
42
+ it "should finish the event" do
43
+ expect(subscriber).to receive(:finish).with("SUCCEEDED", event)
44
+
45
+ subscriber.succeeded(event)
46
+ end
47
+ end
48
+
49
+ describe "#failed" do
50
+ let(:event) { double }
51
+
52
+ it "should finish the event" do
53
+ expect(subscriber).to receive(:finish).with("FAILED", event)
54
+
55
+ subscriber.failed(event)
56
+ end
57
+ end
58
+
59
+ describe "#finish" do
60
+ let(:command) { { "foo" => "?" } }
61
+ let(:event) do
62
+ double(
63
+ :request_id => 2,
64
+ :command_name => :find,
65
+ :database_name => "test"
66
+ )
67
+ end
68
+
69
+ before do
70
+ store = transaction.store("mongo_driver")
71
+ store[2] = command
72
+ end
73
+
74
+ it "should get the query from the store" do
75
+ expect(transaction).to receive(:store).with("mongo_driver").and_return(command)
76
+
77
+ subscriber.finish("SUCCEEDED", event)
78
+ end
79
+
80
+ it "should finish the transaction in the extension" do
81
+ expect(transaction).to receive(:finish_event).with(
82
+ "query.mongodb",
83
+ "find | test | SUCCEEDED",
84
+ Appsignal::Utils.data_generate("foo" => "?"),
85
+ 0
86
+ )
87
+
88
+ subscriber.finish("SUCCEEDED", event)
89
+ end
90
+ end
91
+ end
92
+
93
+ context "without transaction" do
94
+ before do
95
+ allow(Appsignal::Transaction).to receive(:current).and_return(Appsignal::Transaction::NilTransaction.new)
96
+ end
97
+
98
+ it "should not attempt to start an event" do
99
+ expect(Appsignal::Extension).to_not receive(:start_event)
100
+
101
+ subscriber.started(double)
102
+ end
103
+
104
+ it "should not attempt to finish an event" do
105
+ expect(Appsignal::Extension).to_not receive(:finish_event)
106
+
107
+ subscriber.finish("SUCCEEDED", double)
108
+ end
109
+ end
110
+
111
+ context "when appsignal is paused" do
112
+ let(:transaction) { double(:paused? => true, :nil_transaction? => false) }
113
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
114
+
115
+ it "should not attempt to start an event" do
116
+ expect(Appsignal::Extension).to_not receive(:start_event)
117
+
118
+ subscriber.started(double)
119
+ end
120
+
121
+ it "should not attempt to finish an event" do
122
+ expect(Appsignal::Extension).to_not receive(:finish_event)
123
+
124
+ subscriber.finish("SUCCEEDED", double)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,249 @@
1
+ require "appsignal/integrations/object"
2
+
3
+ describe Object do
4
+ describe "#instrument_method" do
5
+ context "with instance method" do
6
+ let(:klass) do
7
+ Class.new do
8
+ def foo
9
+ 1
10
+ end
11
+ appsignal_instrument_method :foo
12
+ end
13
+ end
14
+ let(:instance) { klass.new }
15
+
16
+ context "when active" do
17
+ let(:transaction) { http_request_transaction }
18
+ before do
19
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
20
+ Appsignal.config = project_fixture_config
21
+ end
22
+ after { Appsignal.config = nil }
23
+
24
+ context "with anonymous class" do
25
+ it "instruments the method and calls it" do
26
+ expect(Appsignal.active?).to be_truthy
27
+ expect(transaction).to receive(:start_event)
28
+ expect(transaction).to receive(:finish_event).with \
29
+ "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
30
+ expect(instance.foo).to eq(1)
31
+ end
32
+ end
33
+
34
+ context "with named class" do
35
+ before do
36
+ class NamedClass
37
+ def foo
38
+ 1
39
+ end
40
+ appsignal_instrument_method :foo
41
+ end
42
+ end
43
+ after { Object.send(:remove_const, :NamedClass) }
44
+ let(:klass) { NamedClass }
45
+
46
+ it "instruments the method and calls it" do
47
+ expect(Appsignal.active?).to be_truthy
48
+ expect(transaction).to receive(:start_event)
49
+ expect(transaction).to receive(:finish_event).with \
50
+ "foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
51
+ expect(instance.foo).to eq(1)
52
+ end
53
+ end
54
+
55
+ context "with nested named class" do
56
+ before do
57
+ module MyModule
58
+ module NestedModule
59
+ class NamedClass
60
+ def bar
61
+ 2
62
+ end
63
+ appsignal_instrument_method :bar
64
+ end
65
+ end
66
+ end
67
+ end
68
+ after { Object.send(:remove_const, :MyModule) }
69
+ let(:klass) { MyModule::NestedModule::NamedClass }
70
+
71
+ it "instruments the method and calls it" do
72
+ expect(Appsignal.active?).to be_truthy
73
+ expect(transaction).to receive(:start_event)
74
+ expect(transaction).to receive(:finish_event).with \
75
+ "bar.NamedClass.NestedModule.MyModule.other", nil, nil,
76
+ Appsignal::EventFormatter::DEFAULT
77
+ expect(instance.bar).to eq(2)
78
+ end
79
+ end
80
+
81
+ context "with custom name" do
82
+ let(:klass) do
83
+ Class.new do
84
+ def foo
85
+ 1
86
+ end
87
+ appsignal_instrument_method :foo, :name => "my_method.group"
88
+ end
89
+ end
90
+
91
+ it "instruments with custom name" do
92
+ expect(Appsignal.active?).to be_truthy
93
+ expect(transaction).to receive(:start_event)
94
+ expect(transaction).to receive(:finish_event).with \
95
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
96
+ expect(instance.foo).to eq(1)
97
+ end
98
+ end
99
+
100
+ context "with a method given a block" do
101
+ let(:klass) do
102
+ Class.new do
103
+ def foo
104
+ yield
105
+ end
106
+ appsignal_instrument_method :foo
107
+ end
108
+ end
109
+
110
+ it "should yield the block" do
111
+ expect(instance.foo { 42 }).to eq(42)
112
+ end
113
+ end
114
+ end
115
+
116
+ context "when not active" do
117
+ let(:transaction) { Appsignal::Transaction.current }
118
+
119
+ it "should not instrument, but still call the method" do
120
+ expect(Appsignal.active?).to be_falsy
121
+ expect(transaction).to_not receive(:start_event)
122
+ expect(instance.foo).to eq(1)
123
+ end
124
+ end
125
+ end
126
+
127
+ context "with class method" do
128
+ let(:klass) do
129
+ Class.new do
130
+ def self.bar
131
+ 2
132
+ end
133
+ appsignal_instrument_class_method :bar
134
+ end
135
+ end
136
+
137
+ context "when active" do
138
+ let(:transaction) { http_request_transaction }
139
+ before do
140
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once)
141
+ .and_return(transaction)
142
+ Appsignal.config = project_fixture_config
143
+ end
144
+ after { Appsignal.config = nil }
145
+
146
+ context "with anonymous class" do
147
+ it "instruments the method and calls it" do
148
+ expect(Appsignal.active?).to be_truthy
149
+ expect(transaction).to receive(:start_event)
150
+ expect(transaction).to receive(:finish_event).with \
151
+ "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
152
+ expect(klass.bar).to eq(2)
153
+ end
154
+ end
155
+
156
+ context "with named class" do
157
+ before do
158
+ class NamedClass
159
+ def self.bar
160
+ 2
161
+ end
162
+ appsignal_instrument_class_method :bar
163
+ end
164
+ end
165
+ after { Object.send(:remove_const, :NamedClass) }
166
+ let(:klass) { NamedClass }
167
+
168
+ it "instruments the method and calls it" do
169
+ expect(Appsignal.active?).to be_truthy
170
+ expect(transaction).to receive(:start_event)
171
+ expect(transaction).to receive(:finish_event).with \
172
+ "bar.class_method.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
173
+ expect(klass.bar).to eq(2)
174
+ end
175
+
176
+ context "with nested named class" do
177
+ before do
178
+ module MyModule
179
+ module NestedModule
180
+ class NamedClass
181
+ def self.bar
182
+ 2
183
+ end
184
+ appsignal_instrument_class_method :bar
185
+ end
186
+ end
187
+ end
188
+ end
189
+ after { Object.send(:remove_const, :MyModule) }
190
+ let(:klass) { MyModule::NestedModule::NamedClass }
191
+
192
+ it "instruments the method and calls it" do
193
+ expect(Appsignal.active?).to be_truthy
194
+ expect(transaction).to receive(:start_event)
195
+ expect(transaction).to receive(:finish_event).with \
196
+ "bar.class_method.NamedClass.NestedModule.MyModule.other", nil, nil,
197
+ Appsignal::EventFormatter::DEFAULT
198
+ expect(klass.bar).to eq(2)
199
+ end
200
+ end
201
+ end
202
+
203
+ context "with custom name" do
204
+ let(:klass) do
205
+ Class.new do
206
+ def self.bar
207
+ 2
208
+ end
209
+ appsignal_instrument_class_method :bar, :name => "my_method.group"
210
+ end
211
+ end
212
+
213
+ it "instruments with custom name" do
214
+ expect(Appsignal.active?).to be_truthy
215
+ expect(transaction).to receive(:start_event)
216
+ expect(transaction).to receive(:finish_event).with \
217
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
218
+ expect(klass.bar).to eq(2)
219
+ end
220
+ end
221
+
222
+ context "with a method given a block" do
223
+ let(:klass) do
224
+ Class.new do
225
+ def self.bar
226
+ yield
227
+ end
228
+ appsignal_instrument_class_method :bar
229
+ end
230
+ end
231
+
232
+ it "should yield the block" do
233
+ expect(klass.bar { 42 }).to eq(42)
234
+ end
235
+ end
236
+ end
237
+
238
+ context "when not active" do
239
+ let(:transaction) { Appsignal::Transaction.current }
240
+
241
+ it "should not instrument, but still call the method" do
242
+ expect(Appsignal.active?).to be_falsy
243
+ expect(transaction).to_not receive(:start_event)
244
+ expect(klass.bar).to eq(2)
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,323 @@
1
+ if DependencyHelper.padrino_present?
2
+ describe "Padrino integration" do
3
+ require "appsignal/integrations/padrino"
4
+
5
+ before do
6
+ allow(Appsignal).to receive(:active?).and_return(true)
7
+ allow(Appsignal).to receive(:start).and_return(true)
8
+ allow(Appsignal).to receive(:start_logger).and_return(true)
9
+ end
10
+
11
+ describe Appsignal::Integrations::PadrinoPlugin do
12
+ it "starts AppSignal on init" do
13
+ expect(Appsignal).to receive(:start)
14
+ end
15
+
16
+ it "starts the logger on init" do
17
+ expect(Appsignal).to receive(:start_logger)
18
+ end
19
+
20
+ context "when not active" do
21
+ before { allow(Appsignal).to receive(:active?).and_return(false) }
22
+
23
+ it "does not add the listener middleware to the stack" do
24
+ expect(Padrino).to_not receive(:use)
25
+ end
26
+ end
27
+
28
+ context "when APPSIGNAL_APP_ENV ENV var is provided" do
29
+ it "uses this as the environment" do
30
+ ENV["APPSIGNAL_APP_ENV"] = "custom"
31
+
32
+ # Reset the plugin to pull down the latest data
33
+ Appsignal::Integrations::PadrinoPlugin.init
34
+
35
+ expect(Appsignal.config.env).to eq("custom")
36
+ end
37
+ end
38
+
39
+ context "when APPSIGNAL_APP_ENV ENV var is not provided" do
40
+ it "uses the Padrino environment" do
41
+ # Reset the plugin to pull down the latest data
42
+ Appsignal::Integrations::PadrinoPlugin.init
43
+
44
+ expect(Padrino.env.to_s).to eq("test")
45
+ expect(Appsignal.config.env).to eq(Padrino.env.to_s)
46
+ end
47
+ end
48
+
49
+ after { Appsignal::Integrations::PadrinoPlugin.init }
50
+ end
51
+
52
+ describe Padrino::Routing::InstanceMethods do
53
+ class PadrinoClassWithRouter
54
+ include Padrino::Routing
55
+ end
56
+
57
+ let(:base) { double }
58
+ let(:router) { PadrinoClassWithRouter.new }
59
+ let(:env) { {} }
60
+ # TODO: use an instance double
61
+ let(:settings) { double(:name => "TestApp") }
62
+
63
+ describe "routes" do
64
+ let(:transaction) do
65
+ instance_double "Appsignal::Transaction",
66
+ :set_http_or_background_action => nil,
67
+ :set_http_or_background_queue_start => nil,
68
+ :set_metadata => nil,
69
+ :set_action => nil,
70
+ :set_action_if_nil => nil,
71
+ :set_error => nil,
72
+ :start_event => nil,
73
+ :finish_event => nil,
74
+ :complete => nil
75
+ end
76
+ let(:request_kind) { kind_of(Sinatra::Request) }
77
+ let(:env) do
78
+ {
79
+ "REQUEST_METHOD" => "GET",
80
+ "PATH_INFO" => path,
81
+ "REQUEST_PATH" => path,
82
+ "rack.input" => StringIO.new
83
+ }
84
+ end
85
+ let(:app) do
86
+ Class.new(Padrino::Application) do
87
+ def self.name
88
+ "PadrinoTestApp"
89
+ end
90
+ end
91
+ end
92
+ let(:response) { app.call(env) }
93
+ before do
94
+ allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
95
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
96
+ end
97
+
98
+ RSpec::Matchers.define :match_response do |expected_status, expected_content|
99
+ match do |response|
100
+ status, _headers, content = response
101
+ status == expected_status && content == [expected_content].compact
102
+ end
103
+ end
104
+
105
+ def expect_a_transaction_to_be_created
106
+ expect(Appsignal::Transaction).to receive(:create).with(
107
+ kind_of(String),
108
+ Appsignal::Transaction::HTTP_REQUEST,
109
+ request_kind
110
+ ).and_return(transaction)
111
+
112
+ expect(Appsignal).to receive(:instrument)
113
+ .at_least(:once)
114
+ .with("process_action.padrino")
115
+ .and_call_original
116
+ expect(transaction).to receive(:set_metadata).with("path", path)
117
+ expect(transaction).to receive(:set_metadata).with("method", "GET")
118
+ expect(transaction).to receive(:complete)
119
+ end
120
+
121
+ def expect_no_transaction_to_be_created
122
+ expect(Appsignal::Transaction).to_not receive(:create)
123
+ expect(Appsignal).to_not receive(:instrument)
124
+ end
125
+
126
+ context "when AppSignal is not active" do
127
+ before { allow(Appsignal).to receive(:active?).and_return(false) }
128
+ let(:path) { "/foo" }
129
+ before { app.controllers { get(:foo) { "content" } } }
130
+ after { expect(response).to match_response(200, "content") }
131
+
132
+ it "does not instrument the request" do
133
+ expect_no_transaction_to_be_created
134
+ end
135
+ end
136
+
137
+ context "when AppSignal is active" do
138
+ context "with not existing route" do
139
+ let(:path) { "/404" }
140
+
141
+ it "instruments the request" do
142
+ expect_a_transaction_to_be_created
143
+ # Uses path for action name
144
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/404")
145
+ expect(response).to match_response(404, "<h1>Not Found</h1>")
146
+ end
147
+ end
148
+
149
+ context "when Sinatra tells us it's a static file" do
150
+ let(:path) { "/static" }
151
+ before do
152
+ env["sinatra.static_file"] = true
153
+ expect_any_instance_of(app)
154
+ .to receive(:route_without_appsignal).and_return([200, {}, ["foo"]])
155
+ end
156
+ after { expect(response).to match_response(200, "foo") }
157
+
158
+ it "does not instrument the request" do
159
+ expect_no_transaction_to_be_created
160
+ end
161
+ end
162
+
163
+ # Older Padrino versions don't support `action` (v11.0+)
164
+ context "without #action on Sinatra::Request" do
165
+ let(:path) { "/my_original_path" }
166
+ let(:request_kind) do
167
+ double(
168
+ :params => {},
169
+ :path => path,
170
+ :path_info => path,
171
+ :request_method => "GET",
172
+ :head? => false,
173
+ :route_obj => double(:original_path => "original_path method"),
174
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
175
+ )
176
+ end
177
+ before do
178
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
179
+ app.controllers { get(:my_original_path) { "content" } }
180
+ end
181
+ after { expect(response).to match_response(200, "content") }
182
+
183
+ it "falls back on Sinatra::Request#route_obj.original_path" do
184
+ expect_a_transaction_to_be_created
185
+ expect(transaction)
186
+ .to receive(:set_action_if_nil).with("PadrinoTestApp:original_path method")
187
+ end
188
+ end
189
+
190
+ context "without Sinatra::Request#route_obj.original_path" do
191
+ let(:path) { "/my_original_path" }
192
+ let(:request_kind) do
193
+ double(
194
+ :params => {},
195
+ :path => path,
196
+ :path_info => path,
197
+ :request_method => "GET",
198
+ :head? => false,
199
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
200
+ )
201
+ end
202
+ before do
203
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
204
+ app.controllers { get(:my_original_path) { "content" } }
205
+ end
206
+ after { expect(response).to match_response(200, "content") }
207
+
208
+ it "falls back on app name" do
209
+ expect_a_transaction_to_be_created
210
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp")
211
+ end
212
+ end
213
+
214
+ context "with existing route" do
215
+ context "with an exception in the controller" do
216
+ let(:path) { "/exception" }
217
+ before do
218
+ app.controllers { get(:exception) { raise ExampleException } }
219
+ expect_a_transaction_to_be_created
220
+ end
221
+ after do
222
+ expect { response }.to raise_error(ExampleException)
223
+ end
224
+
225
+ it "sets the action name based on the app name and action name" do
226
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#exception")
227
+ end
228
+
229
+ it "sets the error on the transaction" do
230
+ expect(transaction).to receive(:set_error).with(ExampleException)
231
+ end
232
+ end
233
+
234
+ context "without an exception in the controller" do
235
+ let(:path) { "/" }
236
+ after { expect(response).to match_response(200, "content") }
237
+
238
+ context "with action name as symbol" do
239
+ context "with :index helper" do
240
+ before do
241
+ # :index == "/"
242
+ app.controllers { get(:index) { "content" } }
243
+ end
244
+
245
+ it "sets the action with the app name and action name" do
246
+ expect_a_transaction_to_be_created
247
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#index")
248
+ end
249
+ end
250
+
251
+ context "with custom action name" do
252
+ let(:path) { "/foo" }
253
+ before do
254
+ app.controllers { get(:foo) { "content" } }
255
+ end
256
+
257
+ it "sets the action with the app name and action name" do
258
+ expect_a_transaction_to_be_created
259
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#foo")
260
+ end
261
+ end
262
+ end
263
+
264
+ context "with an action defined with a path" do
265
+ context "with root path" do
266
+ before do
267
+ # :index == "/"
268
+ app.controllers { get("/") { "content" } }
269
+ end
270
+
271
+ it "sets the action with the app name and action path" do
272
+ expect_a_transaction_to_be_created
273
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/")
274
+ end
275
+ end
276
+
277
+ context "with custom path" do
278
+ let(:path) { "/foo" }
279
+ before do
280
+ app.controllers { get("/foo") { "content" } }
281
+ end
282
+
283
+ it "sets the action with the app name and action path" do
284
+ expect_a_transaction_to_be_created
285
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/foo")
286
+ end
287
+ end
288
+ end
289
+
290
+ context "with controller" do
291
+ let(:path) { "/my_controller" }
292
+
293
+ context "with controller as name" do
294
+ before do
295
+ # :index == "/"
296
+ app.controllers(:my_controller) { get(:index) { "content" } }
297
+ end
298
+
299
+ it "sets the action with the app name, controller name and action name" do
300
+ expect_a_transaction_to_be_created
301
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:my_controller#index")
302
+ end
303
+ end
304
+
305
+ context "with controller as path" do
306
+ before do
307
+ # :index == "/"
308
+ app.controllers("/my_controller") { get(:index) { "content" } }
309
+ end
310
+
311
+ it "sets the action with the app name, controller name and action path" do
312
+ expect_a_transaction_to_be_created
313
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:/my_controller#index")
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end