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,168 @@
1
+ require "appsignal/cli"
2
+
3
+ describe Appsignal::CLI::NotifyOfDeploy do
4
+ include CLIHelpers
5
+
6
+ let(:out_stream) { std_stream }
7
+ let(:output) { out_stream.read }
8
+
9
+ define :include_deploy_notification do
10
+ match do |log|
11
+ log.include?("Notifying AppSignal of deploy with: ") &&
12
+ log.include?("AppSignal has been notified of this deploy!")
13
+ end
14
+ end
15
+ define :include_deploy_notification_with do |options|
16
+ match do |log|
17
+ return false unless options
18
+ values = "revision: #{options[:revision]}, user: #{options[:user]}"
19
+ log.include?("Notifying AppSignal of deploy with: #{values}") &&
20
+ log.include?("AppSignal has been notified of this deploy!")
21
+ end
22
+ end
23
+ define :include_config_error do
24
+ match do |log|
25
+ log.include? "Error: No valid config found."
26
+ end
27
+ end
28
+ define :include_missing_options do |options|
29
+ match do |log|
30
+ log.include? "Error: Missing options: #{options.join(", ")}"
31
+ end
32
+ end
33
+
34
+ def run
35
+ capture_stdout(out_stream) do
36
+ run_cli("notify_of_deploy", options)
37
+ end
38
+ end
39
+
40
+ context "without config" do
41
+ let(:config) { Appsignal::Config.new(tmp_dir, "production") }
42
+ let(:options) { {} }
43
+ around do |example|
44
+ Dir.chdir tmp_dir do
45
+ example.run
46
+ end
47
+ end
48
+
49
+ it "prints a config error" do
50
+ expect { run }.to raise_error(SystemExit)
51
+ expect(output).to include_config_error
52
+ expect(output).to_not include_deploy_notification
53
+ end
54
+ end
55
+
56
+ context "with config" do
57
+ let(:config) { project_fixture_config }
58
+ before do
59
+ config[:name] = options[:name] if options[:name]
60
+ stub_api_request config, "markers", :revision => options[:revision],
61
+ :user => options[:user]
62
+ end
63
+ around do |example|
64
+ Dir.chdir project_fixture_path do
65
+ example.run
66
+ end
67
+ end
68
+
69
+ context "without environment" do
70
+ let(:options) { { :environment => "", :revision => "foo", :user => "thijs" } }
71
+ before do
72
+ # Makes the config "active"
73
+ ENV["APPSIGNAL_PUSH_API_KEY"] = "foo"
74
+ end
75
+
76
+ it "requires environment option" do
77
+ expect { run }.to raise_error(SystemExit)
78
+ expect(output).to include_missing_options(%w[environment])
79
+ expect(output).to_not include_deploy_notification
80
+ end
81
+ end
82
+
83
+ context "without known environment" do
84
+ let(:options) { { :environment => "foo" } }
85
+
86
+ it "prints a config error" do
87
+ expect { run }.to raise_error(SystemExit)
88
+ expect(output).to include_config_error
89
+ expect(output).to_not include_missing_options([])
90
+ expect(output).to_not include_deploy_notification
91
+ end
92
+ end
93
+
94
+ context "with known environment" do
95
+ context "without required options" do
96
+ let(:options) { { :environment => "production" } }
97
+
98
+ it "prints a missing required options error" do
99
+ expect { run }.to raise_error(SystemExit)
100
+ expect(output).to_not include_config_error
101
+ expect(output).to include_missing_options(%w[revision user])
102
+ expect(output).to_not include_deploy_notification
103
+ end
104
+
105
+ context "with empty required option" do
106
+ let(:options) { { :environment => "production", :revision => "foo", :user => "" } }
107
+
108
+ it "prints a missing required option error" do
109
+ expect { run }.to raise_error(SystemExit)
110
+ expect(output).to_not include_config_error
111
+ expect(output).to include_missing_options(%w[user])
112
+ expect(output).to_not include_deploy_notification
113
+ end
114
+ end
115
+ end
116
+
117
+ context "with required options" do
118
+ let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs" } }
119
+
120
+ it "notifies of a deploy" do
121
+ run
122
+ expect(output).to_not include_config_error
123
+ expect(output).to_not include_missing_options([])
124
+ expect(output).to include_deploy_notification_with(options)
125
+ end
126
+
127
+ context "with no app name configured" do
128
+ before { ENV["APPSIGNAL_APP_NAME"] = "" }
129
+
130
+ context "without name option" do
131
+ let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs" } }
132
+
133
+ it "requires name option" do
134
+ expect { run }.to raise_error(SystemExit)
135
+ expect(output).to_not include_config_error
136
+ expect(output).to include_missing_options(%w[name])
137
+ expect(output).to_not include_deploy_notification
138
+ end
139
+ end
140
+
141
+ context "with name option" do
142
+ let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs", :name => "foo" } }
143
+
144
+ it "notifies of a deploy with a custom name" do
145
+ run
146
+ expect(output).to_not include_config_error
147
+ expect(output).to_not include_missing_options([])
148
+ expect(output).to include_deploy_notification_with(options)
149
+ end
150
+ end
151
+ end
152
+
153
+ context "with name option" do
154
+ let(:options) do
155
+ { :environment => "production", :revision => "aaaaa", :user => "thijs", :name => "foo" }
156
+ end
157
+
158
+ it "notifies of a deploy with a custom name" do
159
+ run
160
+ expect(output).to_not include_config_error
161
+ expect(output).to_not include_missing_options([])
162
+ expect(output).to include_deploy_notification_with(options)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,56 @@
1
+ require "appsignal/cli"
2
+
3
+ describe Appsignal::CLI do
4
+ let(:out_stream) { std_stream }
5
+ let(:output) { out_stream.read }
6
+ let(:cli) { Appsignal::CLI }
7
+ before { allow(Dir).to receive(:pwd).and_return(project_fixture_path) }
8
+
9
+ it "should print the help with no arguments, -h and --help" do
10
+ [nil, "-h", "--help"].each do |arg|
11
+ expect do
12
+ capture_stdout(out_stream) do
13
+ cli.run([arg].compact)
14
+ end
15
+ end.to raise_error(SystemExit)
16
+
17
+ expect(output).to include "appsignal <command> [options]"
18
+ expect(output).to include \
19
+ "Available commands: demo, diagnose, install, notify_of_deploy"
20
+ end
21
+ end
22
+
23
+ it "should print the version with -v and --version" do
24
+ ["-v", "--version"].each do |arg|
25
+ expect do
26
+ capture_stdout(out_stream) do
27
+ cli.run([arg])
28
+ end
29
+ end.to raise_error(SystemExit)
30
+
31
+ expect(output).to include "AppSignal"
32
+ expect(output).to include "."
33
+ end
34
+ end
35
+
36
+ it "should print a notice if a command does not exist" do
37
+ expect do
38
+ capture_stdout(out_stream) do
39
+ cli.run(["nonsense"])
40
+ end
41
+ end.to raise_error(SystemExit)
42
+
43
+ expect(output).to include "Command 'nonsense' does not exist, run "\
44
+ "appsignal -h to see the help"
45
+ end
46
+
47
+ describe "diagnose" do
48
+ it "should call Appsignal::Diagnose.install" do
49
+ expect(Appsignal::CLI::Diagnose).to receive(:run)
50
+
51
+ cli.run([
52
+ "diagnose"
53
+ ])
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,637 @@
1
+ describe Appsignal::Config do
2
+ describe "#initialize" do
3
+ subject { config.env }
4
+
5
+ describe "environment" do
6
+ context "when environment is nil" do
7
+ let(:config) { described_class.new("", "") }
8
+
9
+ it "sets an empty string" do
10
+ expect(subject).to eq("")
11
+ end
12
+ end
13
+
14
+ context "when environment is given" do
15
+ let(:config) { described_class.new("", "my_env") }
16
+
17
+ it "sets the environment" do
18
+ expect(subject).to eq("my_env")
19
+ end
20
+
21
+ context "with APPSIGNAL_APP_ENV environment variable" do
22
+ before { ENV["APPSIGNAL_APP_ENV"] = "my_env_env" }
23
+
24
+ it "uses the environment variable" do
25
+ expect(subject).to eq("my_env_env")
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "config based on the system" do
33
+ let(:config) { project_fixture_config(:none) }
34
+
35
+ describe ":active" do
36
+ subject { config[:active] }
37
+
38
+ context "with APPSIGNAL_PUSH_API_KEY env variable" do
39
+ before { ENV["APPSIGNAL_PUSH_API_KEY"] = "abc" }
40
+
41
+ it "becomes active" do
42
+ expect(subject).to be_truthy
43
+ end
44
+ end
45
+
46
+ context "without APPSIGNAL_PUSH_API_KEY env variable" do
47
+ it "remains inactive" do
48
+ expect(subject).to be_falsy
49
+ end
50
+ end
51
+ end
52
+
53
+ describe ":log" do
54
+ subject { config[:log] }
55
+
56
+ context "when running on Heroku" do
57
+ around { |example| recognize_as_heroku { example.run } }
58
+
59
+ it "is set to stdout" do
60
+ expect(subject).to eq("stdout")
61
+ end
62
+ end
63
+
64
+ context "when not running on Heroku" do
65
+ it "is set to file" do
66
+ expect(subject).to eq("file")
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "initial config" do
73
+ let(:config) do
74
+ described_class.new(
75
+ "non-existing-path",
76
+ "production",
77
+ :push_api_key => "abc",
78
+ :name => "TestApp",
79
+ :active => true
80
+ )
81
+ end
82
+
83
+ it "merges with the default config" do
84
+ expect(config.config_hash).to eq(
85
+ :debug => false,
86
+ :log => "file",
87
+ :ignore_actions => [],
88
+ :ignore_errors => [],
89
+ :ignore_namespaces => [],
90
+ :filter_parameters => [],
91
+ :instrument_net_http => true,
92
+ :instrument_redis => true,
93
+ :instrument_sequel => true,
94
+ :skip_session_data => false,
95
+ :send_params => true,
96
+ :endpoint => "https://push.appsignal.com",
97
+ :push_api_key => "abc",
98
+ :name => "TestApp",
99
+ :active => true,
100
+ :enable_frontend_error_catching => false,
101
+ :frontend_error_catching_path => "/appsignal_error_catcher",
102
+ :enable_allocation_tracking => true,
103
+ :enable_gc_instrumentation => false,
104
+ :enable_host_metrics => true,
105
+ :enable_minutely_probes => false,
106
+ :hostname => Socket.gethostname,
107
+ :ca_file_path => File.join(resources_dir, "cacert.pem"),
108
+ :dns_servers => [],
109
+ :files_world_accessible => true
110
+ )
111
+ end
112
+
113
+ describe "overriding system detected config" do
114
+ describe ":running_in_container" do
115
+ let(:config) do
116
+ described_class.new(
117
+ "non-existing-path",
118
+ "production",
119
+ :running_in_container => true
120
+ )
121
+ end
122
+ subject { config[:running_in_container] }
123
+
124
+ it "overrides system detected config" do
125
+ expect(subject).to be_truthy
126
+ end
127
+ end
128
+
129
+ describe ":active" do
130
+ subject { config[:active] }
131
+
132
+ context "with APPSIGNAL_PUSH_API_KEY env variable" do
133
+ let(:config) do
134
+ described_class.new(
135
+ "non-existing-path",
136
+ "production",
137
+ :active => false
138
+ )
139
+ end
140
+ before { ENV["APPSIGNAL_PUSH_API_KEY"] = "abc" }
141
+
142
+ it "sets given config rather than env variable" do
143
+ expect(subject).to be_falsy
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ context "when root path is nil" do
151
+ let(:config) { described_class.new(nil, "production") }
152
+
153
+ it "is not valid or active" do
154
+ expect(config.valid?).to be_falsy
155
+ expect(config.active?).to be_falsy
156
+ end
157
+ end
158
+
159
+ context "without config file" do
160
+ let(:config) { described_class.new(tmp_dir, "production") }
161
+
162
+ it "is not valid or active" do
163
+ expect(config.valid?).to be_falsy
164
+ expect(config.active?).to be_falsy
165
+ end
166
+ end
167
+
168
+ context "with a config file" do
169
+ let(:config) { project_fixture_config("production") }
170
+
171
+ it "is not valid or active" do
172
+ expect(config.valid?).to be_truthy
173
+ expect(config.active?).to be_truthy
174
+ end
175
+
176
+ it "does not log an error" do
177
+ expect_any_instance_of(Logger).to_not receive(:error)
178
+ config
179
+ end
180
+
181
+ describe "overriding system and defaults config" do
182
+ let(:config) do
183
+ described_class.new(
184
+ "non-existing-path",
185
+ "production",
186
+ :running_in_container => true,
187
+ :debug => true
188
+ )
189
+ end
190
+
191
+ it "overrides system detected and defaults config" do
192
+ expect(config[:running_in_container]).to be_truthy
193
+ expect(config[:debug]).to be_truthy
194
+ end
195
+ end
196
+
197
+ context "with the env name as a symbol" do
198
+ let(:config) { project_fixture_config(:production) }
199
+
200
+ it "loads the config" do
201
+ expect(config.valid?).to be_truthy
202
+ expect(config.active?).to be_truthy
203
+
204
+ expect(config[:push_api_key]).to eq("abc")
205
+ end
206
+ end
207
+
208
+ context "without the selected env" do
209
+ let(:config) { project_fixture_config("nonsense") }
210
+
211
+ it "is not valid or active" do
212
+ expect(config.valid?).to be_falsy
213
+ expect(config.active?).to be_falsy
214
+ end
215
+
216
+ it "logs an error" do
217
+ expect_any_instance_of(Logger).to receive(:error).once
218
+ .with("Not loading from config file: config for 'nonsense' not found")
219
+ expect_any_instance_of(Logger).to receive(:error).once
220
+ .with("Push api key not set after loading config")
221
+ config
222
+ end
223
+ end
224
+
225
+ describe "support for old config keys" do
226
+ let(:config) { project_fixture_config(env, {}, test_logger(log)) }
227
+ let(:log) { StringIO.new }
228
+
229
+ describe ":api_key" do
230
+ context "without :push_api_key" do
231
+ let(:env) { "old_config" }
232
+
233
+ it "sets the :push_api_key with the old :api_key value" do
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'"
238
+ end
239
+ end
240
+
241
+ context "with :push_api_key" do
242
+ let(:env) { "old_config_mixed_with_new_config" }
243
+
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'"
249
+ end
250
+ end
251
+ end
252
+
253
+ describe ":ignore_exceptions" do
254
+ context "without :ignore_errors" do
255
+ let(:env) { "old_config" }
256
+
257
+ it "sets :ignore_errors with the old :ignore_exceptions value" do
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'"
262
+ end
263
+ end
264
+
265
+ context "with :ignore_errors" do
266
+ let(:env) { "old_config_mixed_with_new_config" }
267
+
268
+ it "ignores the :ignore_exceptions config" do
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'"
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ context "with config in the environment" do
280
+ let(:config) do
281
+ described_class.new(
282
+ "non-existing-path",
283
+ "production",
284
+ :running_in_container => true,
285
+ :debug => true
286
+ )
287
+ end
288
+ before do
289
+ ENV["APPSIGNAL_RUNNING_IN_CONTAINER"] = "true"
290
+ ENV["APPSIGNAL_PUSH_API_KEY"] = "aaa-bbb-ccc"
291
+ ENV["APPSIGNAL_ACTIVE"] = "true"
292
+ ENV["APPSIGNAL_APP_NAME"] = "App name"
293
+ ENV["APPSIGNAL_DEBUG"] = "true"
294
+ ENV["APPSIGNAL_IGNORE_ACTIONS"] = "action1,action2"
295
+ ENV["APPSIGNAL_IGNORE_ERRORS"] = "ExampleStandardError,AnotherError"
296
+ ENV["APPSIGNAL_IGNORE_NAMESPACES"] = "admin,private_namespace"
297
+ ENV["APPSIGNAL_INSTRUMENT_NET_HTTP"] = "false"
298
+ ENV["APPSIGNAL_INSTRUMENT_REDIS"] = "false"
299
+ ENV["APPSIGNAL_INSTRUMENT_SEQUEL"] = "false"
300
+ ENV["APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = "false"
301
+ end
302
+
303
+ it "overrides config with environment values" do
304
+ expect(config.valid?).to be_truthy
305
+ expect(config.active?).to be_truthy
306
+
307
+ expect(config[:running_in_container]).to be_truthy
308
+ expect(config[:push_api_key]).to eq "aaa-bbb-ccc"
309
+ expect(config[:active]).to eq(true)
310
+ expect(config[:name]).to eq "App name"
311
+ expect(config[:debug]).to eq(true)
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]
315
+ expect(config[:instrument_net_http]).to eq(false)
316
+ expect(config[:instrument_redis]).to eq(false)
317
+ expect(config[:instrument_sequel]).to eq(false)
318
+ expect(config[:files_world_accessible]).to eq(false)
319
+ end
320
+
321
+ context "with mixed case `true` env variables values" do
322
+ before do
323
+ ENV["APPSIGNAL_DEBUG"] = "TRUE"
324
+ ENV["APPSIGNAL_INSTRUMENT_SEQUEL"] = "True"
325
+ end
326
+
327
+ it "accepts mixed case `true` values" do
328
+ expect(config[:debug]).to eq(true)
329
+ expect(config[:instrument_sequel]).to eq(true)
330
+ end
331
+ end
332
+ end
333
+
334
+ describe "config keys" do
335
+ describe ":endpoint" do
336
+ subject { config[:endpoint] }
337
+
338
+ context "with an pre-0.12-style endpoint" do
339
+ let(:config) do
340
+ project_fixture_config("production", :endpoint => "https://push.appsignal.com/1")
341
+ end
342
+
343
+ it "strips off the path" do
344
+ expect(subject).to eq "https://push.appsignal.com"
345
+ end
346
+ end
347
+
348
+ context "with a non-standard port" do
349
+ let(:config) { project_fixture_config("production", :endpoint => "http://localhost:4567") }
350
+
351
+ it "keeps the port" do
352
+ expect(subject).to eq "http://localhost:4567"
353
+ end
354
+ end
355
+ end
356
+ end
357
+
358
+ describe "#[]" do
359
+ let(:config) { project_fixture_config(:none, :push_api_key => "foo") }
360
+
361
+ context "with existing key" do
362
+ it "gets the value" do
363
+ expect(config[:push_api_key]).to eq "foo"
364
+ end
365
+ end
366
+
367
+ context "without existing key" do
368
+ it "returns nil" do
369
+ expect(config[:nonsense]).to be_nil
370
+ end
371
+ end
372
+ end
373
+
374
+ describe "#[]=" do
375
+ let(:config) { project_fixture_config(:none) }
376
+
377
+ context "with existing key" do
378
+ it "changes the value" do
379
+ expect(config[:push_api_key]).to be_nil
380
+ config[:push_api_key] = "abcde"
381
+ expect(config[:push_api_key]).to eq "abcde"
382
+ end
383
+ end
384
+
385
+ context "with new key" do
386
+ it "sets the value" do
387
+ expect(config[:foo]).to be_nil
388
+ config[:foo] = "bar"
389
+ expect(config[:foo]).to eq "bar"
390
+ end
391
+ end
392
+ end
393
+
394
+ describe "#write_to_environment" do
395
+ let(:config) { project_fixture_config(:production) }
396
+ before do
397
+ config[:http_proxy] = "http://localhost"
398
+ config[:ignore_actions] = %w[action1 action2]
399
+ config[:ignore_errors] = %w[ExampleStandardError AnotherError]
400
+ config[:ignore_namespaces] = %w[admin private_namespace]
401
+ config[:log] = "stdout"
402
+ config[:log_path] = "/tmp"
403
+ config[:hostname] = "app1.local"
404
+ config[:filter_parameters] = %w[password confirm_password]
405
+ config[:running_in_container] = false
406
+ config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
407
+ config.write_to_environment
408
+ end
409
+
410
+ it "writes the current config to environment variables" do
411
+ expect(ENV["_APPSIGNAL_ACTIVE"]).to eq "true"
412
+ expect(ENV["_APPSIGNAL_APP_PATH"]).to end_with("spec/support/project_fixture")
413
+ expect(ENV["_APPSIGNAL_AGENT_PATH"]).to end_with("/ext")
414
+ expect(ENV["_APPSIGNAL_DEBUG_LOGGING"]).to eq "false"
415
+ expect(ENV["_APPSIGNAL_LOG"]).to eq "stdout"
416
+ expect(ENV["_APPSIGNAL_LOG_FILE_PATH"]).to end_with("/tmp/appsignal.log")
417
+ expect(ENV["_APPSIGNAL_PUSH_API_ENDPOINT"]).to eq "https://push.appsignal.com"
418
+ expect(ENV["_APPSIGNAL_PUSH_API_KEY"]).to eq "abc"
419
+ expect(ENV["_APPSIGNAL_APP_NAME"]).to eq "TestApp"
420
+ expect(ENV["_APPSIGNAL_ENVIRONMENT"]).to eq "production"
421
+ expect(ENV["_APPSIGNAL_AGENT_VERSION"]).to eq Appsignal::Extension.agent_version
422
+ expect(ENV["_APPSIGNAL_LANGUAGE_INTEGRATION_VERSION"]).to eq "ruby-#{Appsignal::VERSION}"
423
+ expect(ENV["_APPSIGNAL_HTTP_PROXY"]).to eq "http://localhost"
424
+ expect(ENV["_APPSIGNAL_IGNORE_ACTIONS"]).to eq "action1,action2"
425
+ expect(ENV["_APPSIGNAL_IGNORE_ERRORS"]).to eq "ExampleStandardError,AnotherError"
426
+ expect(ENV["_APPSIGNAL_IGNORE_NAMESPACES"]).to eq "admin,private_namespace"
427
+ expect(ENV["_APPSIGNAL_FILTER_PARAMETERS"]).to eq "password,confirm_password"
428
+ expect(ENV["_APPSIGNAL_SEND_PARAMS"]).to eq "true"
429
+ expect(ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"]).to eq "false"
430
+ expect(ENV["_APPSIGNAL_ENABLE_HOST_METRICS"]).to eq "true"
431
+ expect(ENV["_APPSIGNAL_ENABLE_MINUTELY_PROBES"]).to eq "false"
432
+ expect(ENV["_APPSIGNAL_HOSTNAME"]).to eq "app1.local"
433
+ expect(ENV["_APPSIGNAL_PROCESS_NAME"]).to include "rspec"
434
+ expect(ENV["_APPSIGNAL_CA_FILE_PATH"]).to eq File.join(resources_dir, "cacert.pem")
435
+ expect(ENV["_APPSIGNAL_DNS_SERVERS"]).to eq "8.8.8.8,8.8.4.4"
436
+ expect(ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]).to eq "true"
437
+ expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIR_PATH")
438
+ end
439
+
440
+ context "with :working_dir_path" do
441
+ before do
442
+ config[:working_dir_path] = "/tmp/appsignal2"
443
+ config.write_to_environment
444
+ end
445
+
446
+ it "sets the modified :working_dir_path" do
447
+ expect(ENV["_APPSIGNAL_WORKING_DIR_PATH"]).to eq "/tmp/appsignal2"
448
+ end
449
+ end
450
+ end
451
+
452
+ describe "#log_file_path" do
453
+ let(:out_stream) { std_stream }
454
+ let(:output) { out_stream.read }
455
+ let(:config) { project_fixture_config("production", :log_path => log_path) }
456
+ subject { capture_stdout(out_stream) { config.log_file_path } }
457
+
458
+ context "when path is writable" do
459
+ let(:log_path) { File.join(tmp_dir, "writable-path") }
460
+ before { FileUtils.mkdir_p(log_path, :mode => 0o755) }
461
+ after { FileUtils.rm_rf(log_path) }
462
+
463
+ it "returns log file path" do
464
+ expect(subject).to eq File.join(log_path, "appsignal.log")
465
+ end
466
+
467
+ it "prints no warning" do
468
+ subject
469
+ expect(output).to be_empty
470
+ end
471
+ end
472
+
473
+ shared_examples "#log_file_path: tmp path" do
474
+ let(:system_tmp_dir) { described_class.system_tmp_dir }
475
+ before { FileUtils.mkdir_p(system_tmp_dir) }
476
+ after { FileUtils.rm_rf(system_tmp_dir) }
477
+
478
+ context "when the /tmp fallback path is writable" do
479
+ before { FileUtils.chmod(0o777, system_tmp_dir) }
480
+
481
+ it "returns returns the tmp location" do
482
+ expect(subject).to eq(File.join(system_tmp_dir, "appsignal.log"))
483
+ end
484
+
485
+ it "prints a warning" do
486
+ subject
487
+ expect(output).to include "appsignal: Unable to log to '#{log_path}'. "\
488
+ "Logging to '#{system_tmp_dir}' instead."
489
+ end
490
+ end
491
+
492
+ context "when the /tmp fallback path is not writable" do
493
+ before { FileUtils.chmod(0o555, system_tmp_dir) }
494
+
495
+ it "returns nil" do
496
+ expect(subject).to be_nil
497
+ end
498
+
499
+ it "prints a warning" do
500
+ subject
501
+ expect(output).to include "appsignal: Unable to log to '#{log_path}' "\
502
+ "or the '#{system_tmp_dir}' fallback."
503
+ end
504
+ end
505
+ end
506
+
507
+ context "when path is nil" do
508
+ let(:log_path) { nil }
509
+
510
+ context "when root_path is nil" do
511
+ before { allow(config).to receive(:root_path).and_return(nil) }
512
+
513
+ include_examples "#log_file_path: tmp path"
514
+ end
515
+
516
+ context "when root_path is set" do
517
+ it "returns returns the project log location" do
518
+ expect(subject).to eq File.join(config.root_path, "log/appsignal.log")
519
+ end
520
+
521
+ it "prints no warning" do
522
+ subject
523
+ expect(output).to be_empty
524
+ end
525
+ end
526
+ end
527
+
528
+ context "when path does not exist" do
529
+ let(:log_path) { "/non-existing" }
530
+
531
+ include_examples "#log_file_path: tmp path"
532
+ end
533
+
534
+ context "when path is not writable" do
535
+ let(:log_path) { File.join(tmp_dir, "not-writable-path") }
536
+ before { FileUtils.mkdir_p(log_path, :mode => 0o555) }
537
+ after { FileUtils.rm_rf(log_path) }
538
+
539
+ include_examples "#log_file_path: tmp path"
540
+ end
541
+
542
+ context "when path is a symlink" do
543
+ context "when linked path does not exist" do
544
+ let(:real_path) { File.join(tmp_dir, "real-path") }
545
+ let(:log_path) { File.join(tmp_dir, "symlink-path") }
546
+ before { File.symlink(real_path, log_path) }
547
+ after { FileUtils.rm(log_path) }
548
+
549
+ include_examples "#log_file_path: tmp path"
550
+ end
551
+
552
+ context "when linked path exists" do
553
+ context "when linked path is not writable" do
554
+ let(:real_path) { File.join(tmp_dir, "real-path") }
555
+ let(:log_path) { File.join(tmp_dir, "symlink-path") }
556
+ before do
557
+ FileUtils.mkdir_p(real_path)
558
+ FileUtils.chmod(0o444, real_path)
559
+ File.symlink(real_path, log_path)
560
+ end
561
+ after do
562
+ FileUtils.rm_rf(real_path)
563
+ FileUtils.rm(log_path)
564
+ end
565
+
566
+ include_examples "#log_file_path: tmp path"
567
+ end
568
+
569
+ context "when linked path is writable" do
570
+ let(:real_path) { File.join(tmp_dir, "real-path") }
571
+ let(:log_path) { File.join(tmp_dir, "symlink-path") }
572
+ before do
573
+ FileUtils.mkdir_p(real_path)
574
+ File.symlink(real_path, log_path)
575
+ end
576
+ after do
577
+ FileUtils.rm_rf(real_path)
578
+ FileUtils.rm(log_path)
579
+ end
580
+
581
+ it "returns real path of log path" do
582
+ expect(subject).to eq(File.join(real_path, "appsignal.log"))
583
+ end
584
+ end
585
+ end
586
+ end
587
+ end
588
+
589
+ describe ".system_tmp_dir" do
590
+ before do
591
+ # To counteract the stub in spec_helper
592
+ expect(Appsignal::Config).to receive(:system_tmp_dir).and_call_original
593
+ end
594
+
595
+ context "when on a *NIX OS" do
596
+ before do
597
+ expect(Gem).to receive(:win_platform?).and_return(false)
598
+ end
599
+
600
+ it "returns the system's tmp dir" do
601
+ expect(described_class.system_tmp_dir).to eq(File.realpath("/tmp"))
602
+ end
603
+ end
604
+
605
+ context "when on Microsoft Windows" do
606
+ before do
607
+ expect(Gem).to receive(:win_platform?).and_return(true)
608
+ end
609
+
610
+ it "returns the system's tmp dir" do
611
+ expect(described_class.system_tmp_dir).to eq(Dir.tmpdir)
612
+ end
613
+ end
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
637
+ end