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,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