appsignal 0.6.7 → 0.7.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +3 -2
  3. data/.travis.yml +11 -11
  4. data/CHANGELOG.md +6 -0
  5. data/README.md +26 -0
  6. data/Rakefile +32 -3
  7. data/appsignal.gemspec +25 -16
  8. data/gemfiles/no_dependencies.gemfile +3 -0
  9. data/gemfiles/{3.0.gemfile → rails-3.0.gemfile} +2 -1
  10. data/gemfiles/{3.1.gemfile → rails-3.1.gemfile} +2 -1
  11. data/gemfiles/{3.2.gemfile → rails-3.2.gemfile} +2 -1
  12. data/gemfiles/{4.0.gemfile → rails-4.0.gemfile} +2 -1
  13. data/gemfiles/sinatra.gemfile +5 -0
  14. data/lib/appsignal.rb +30 -25
  15. data/lib/appsignal/agent.rb +37 -17
  16. data/lib/appsignal/aggregator.rb +2 -4
  17. data/lib/appsignal/aggregator/middleware.rb +4 -0
  18. data/lib/appsignal/aggregator/middleware/action_view_sanitizer.rb +23 -0
  19. data/lib/appsignal/aggregator/middleware/active_record_sanitizer.rb +64 -0
  20. data/lib/appsignal/aggregator/middleware/chain.rb +101 -0
  21. data/lib/appsignal/aggregator/middleware/delete_blanks.rb +16 -0
  22. data/lib/appsignal/aggregator/post_processor.rb +21 -18
  23. data/lib/appsignal/auth_check.rb +7 -21
  24. data/lib/appsignal/capistrano.rb +1 -36
  25. data/lib/appsignal/cli.rb +30 -46
  26. data/lib/appsignal/config.rb +46 -61
  27. data/lib/appsignal/integrations/capistrano.rb +44 -0
  28. data/lib/appsignal/{careful_logger.rb → integrations/capistrano/careful_logger.rb} +2 -0
  29. data/lib/appsignal/integrations/passenger.rb +6 -6
  30. data/lib/appsignal/integrations/rails.rb +33 -0
  31. data/lib/appsignal/integrations/sinatra.rb +20 -0
  32. data/lib/appsignal/marker.rb +9 -10
  33. data/lib/appsignal/rack/instrumentation.rb +28 -0
  34. data/lib/appsignal/rack/listener.rb +33 -0
  35. data/lib/appsignal/transaction.rb +18 -12
  36. data/lib/appsignal/transaction/formatter.rb +96 -0
  37. data/lib/appsignal/transaction/params_sanitizer.rb +80 -78
  38. data/lib/appsignal/transmitter.rb +10 -9
  39. data/lib/appsignal/version.rb +1 -1
  40. data/lib/generators/appsignal/appsignal_generator.rb +20 -21
  41. data/lib/generators/appsignal/templates/appsignal.yml +15 -21
  42. data/spec/{appsignal → lib/appsignal}/agent_spec.rb +69 -1
  43. data/spec/lib/appsignal/aggregator/middleware/action_view_sanitizer_spec.rb +32 -0
  44. data/spec/lib/appsignal/aggregator/middleware/active_record_sanitizer_spec.rb +215 -0
  45. data/spec/{appsignal → lib/appsignal/aggregator}/middleware/chain_spec.rb +5 -5
  46. data/spec/{appsignal → lib/appsignal/aggregator}/middleware/delete_blanks_spec.rb +2 -2
  47. data/spec/{appsignal → lib/appsignal}/aggregator/post_processor_spec.rb +15 -6
  48. data/spec/{appsignal → lib/appsignal}/aggregator_spec.rb +4 -1
  49. data/spec/{appsignal → lib/appsignal}/auth_check_spec.rb +8 -23
  50. data/spec/{appsignal → lib/appsignal}/cli_spec.rb +65 -66
  51. data/spec/lib/appsignal/config_spec.rb +132 -0
  52. data/spec/lib/appsignal/integrations/capistrano_spec.rb +123 -0
  53. data/spec/{appsignal → lib/appsignal}/integrations/passenger_spec.rb +0 -1
  54. data/spec/lib/appsignal/integrations/rails_spec.rb +38 -0
  55. data/spec/lib/appsignal/integrations/sinatra_spec.rb +43 -0
  56. data/spec/{appsignal → lib/appsignal}/marker_spec.rb +20 -23
  57. data/spec/lib/appsignal/rack/instrumentation_spec.rb +49 -0
  58. data/spec/{appsignal → lib/appsignal/rack}/listener_spec.rb +39 -6
  59. data/spec/{appsignal/transaction/transaction_formatter_spec.rb → lib/appsignal/transaction/formatter_spec.rb} +29 -6
  60. data/spec/{appsignal → lib/appsignal}/transaction/params_sanitizer_spec.rb +13 -12
  61. data/spec/{appsignal → lib/appsignal}/transaction_spec.rb +52 -7
  62. data/spec/{appsignal → lib/appsignal}/transmitter_spec.rb +27 -20
  63. data/spec/lib/appsignal_spec.rb +230 -0
  64. data/spec/lib/generators/appsignal/appsignal_generator_spec.rb +166 -0
  65. data/spec/lib/tmp/config/appsignal.yml +2 -0
  66. data/spec/spec_helper.rb +29 -20
  67. data/spec/support/delegate_matcher.rb +0 -1
  68. data/spec/support/fixtures/generated_config.yml +20 -0
  69. data/{log/.gitkeep → spec/support/fixtures/uploaded_file.txt} +0 -0
  70. data/spec/support/helpers/config_helpers.rb +24 -0
  71. data/spec/support/helpers/notification_helpers.rb +0 -2
  72. data/spec/support/helpers/transaction_helpers.rb +17 -2
  73. data/spec/support/project_fixture/config/appsignal.yml +18 -0
  74. data/spec/support/project_fixture/log/.gitkeep +0 -0
  75. data/spec/support/rails/my_app.rb +6 -0
  76. metadata +99 -83
  77. data/config/appsignal.yml +0 -10
  78. data/lib/appsignal/listener.rb +0 -21
  79. data/lib/appsignal/middleware.rb +0 -3
  80. data/lib/appsignal/middleware/action_view_sanitizer.rb +0 -21
  81. data/lib/appsignal/middleware/active_record_sanitizer.rb +0 -62
  82. data/lib/appsignal/middleware/chain.rb +0 -99
  83. data/lib/appsignal/middleware/delete_blanks.rb +0 -12
  84. data/lib/appsignal/railtie.rb +0 -37
  85. data/lib/appsignal/to_appsignal_hash.rb +0 -21
  86. data/lib/appsignal/transaction/transaction_formatter.rb +0 -67
  87. data/spec/appsignal/capistrano_spec.rb +0 -81
  88. data/spec/appsignal/config_spec.rb +0 -177
  89. data/spec/appsignal/inactive_railtie_spec.rb +0 -32
  90. data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +0 -27
  91. data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +0 -212
  92. data/spec/appsignal/railtie_spec.rb +0 -74
  93. data/spec/appsignal/to_appsignal_hash_spec.rb +0 -29
  94. data/spec/appsignal_spec.rb +0 -195
  95. data/spec/generators/appsignal/appsignal_generator_spec.rb +0 -181
@@ -1,21 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Appsignal::Transmitter do
4
- let(:_80beans_) { 'http://www.80beans.com' }
4
+ let(:config) { project_fixture_config }
5
5
  let(:action) { 'action' }
6
- let(:klass) { Appsignal::Transmitter }
7
- let(:instance) { klass.new(_80beans_, action, :the_api_key) }
8
- subject { instance }
6
+ let(:instance) { Appsignal::Transmitter.new(action, config) }
9
7
 
10
8
  describe "#uri" do
11
- it "returns the uri" do
12
- Socket.stub(:gethostname => 'app1.local')
13
- uri = subject.uri.to_s
14
- uri.should include "http://www.80beans.com/action?"
15
- uri.should include "api_key=the_api_key"
16
- uri.should include "hostname=app1.local"
17
- uri.should include "gem_version=#{Appsignal::VERSION}"
18
- end
9
+ before { Socket.stub(:gethostname => 'app1.local') }
10
+
11
+ subject { instance.uri.to_s }
12
+
13
+ it { should include 'https://push.appsignal.com/1/action?' }
14
+ it { should include 'api_key=abc' }
15
+ it { should include 'hostname=app1.local' }
16
+ it { should include 'name=TestApp' }
17
+ it { should include 'environment=production' }
18
+ it { should include "gem_version=#{Appsignal::VERSION}" }
19
19
  end
20
20
 
21
21
  describe "#transmit" do
@@ -31,12 +31,17 @@ describe Appsignal::Transmitter do
31
31
  describe "#http_post" do
32
32
  it "calls Net::HTTP.post_form with the correct params" do
33
33
  post = double(:post)
34
- post.should_receive(:[]=).
35
- with('Content-Type', 'application/json; charset=UTF-8')
36
- post.should_receive(:[]=).
37
- with('Content-Encoding', 'gzip')
38
- post.should_receive(:body=).
39
- with(Zlib::Deflate.deflate("{\"the\":\"payload\"}", Zlib::BEST_SPEED))
34
+ post.should_receive(:[]=).with(
35
+ 'Content-Type',
36
+ 'application/json; charset=UTF-8'
37
+ )
38
+ post.should_receive(:[]=).with(
39
+ 'Content-Encoding',
40
+ 'gzip'
41
+ )
42
+ post.should_receive(:body=).with(
43
+ Zlib::Deflate.deflate("{\"the\":\"payload\"}", Zlib::BEST_SPEED)
44
+ )
40
45
  Socket.stub(:gethostname => 'app1.local')
41
46
 
42
47
  Net::HTTP::Post.should_receive(:new).with(
@@ -57,14 +62,16 @@ describe Appsignal::Transmitter do
57
62
  subject { instance.send(:http_client) }
58
63
 
59
64
  context "with a http uri" do
60
- it { should be_instance_of(Net::HTTP) }
65
+ let(:config) { project_fixture_config('test') }
61
66
 
67
+ it { should be_instance_of(Net::HTTP) }
62
68
  its(:use_ssl?) { should be_false }
63
69
  end
64
70
 
65
71
  context "with a https uri" do
66
- let(:instance) { klass.new('https://www.80beans.com', action, :the_api_key) }
72
+ let(:config) { project_fixture_config('production') }
67
73
 
74
+ it { should be_instance_of(Net::HTTP) }
68
75
  its(:use_ssl?) { should be_true }
69
76
  its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER }
70
77
  its(:ca_file) { Appsignal::Transmitter::CA_FILE_PATH }
@@ -0,0 +1,230 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal do
4
+ before do
5
+ # Make sure we have a clean state because we want to test
6
+ # initialization here.
7
+ Appsignal.agent.shutdown if Appsignal.agent
8
+ Appsignal.config = nil
9
+ Appsignal.agent = nil
10
+ end
11
+
12
+ let(:transaction) { regular_transaction }
13
+
14
+ describe ".config=" do
15
+ it "should set the config" do
16
+ config = project_fixture_config
17
+ Appsignal.logger.should_not_receive(:level=)
18
+
19
+ Appsignal.config = config
20
+ Appsignal.config.should == config
21
+ end
22
+ end
23
+
24
+ describe ".start" do
25
+ it "should do nothing when config is not loaded" do
26
+ Appsignal.logger.should_receive(:error).with(
27
+ "Can't start, no config loaded"
28
+ )
29
+ Appsignal.start
30
+ Appsignal.agent.should be_nil
31
+ end
32
+
33
+ context "when config is loaded" do
34
+ before { Appsignal.config = project_fixture_config }
35
+
36
+ it "should start an agent" do
37
+ Appsignal.start
38
+ Appsignal.agent.should be_a Appsignal::Agent
39
+ Appsignal.logger.level.should == Logger::INFO
40
+ end
41
+ end
42
+
43
+ context "with debug logging" do
44
+ before { Appsignal.config = project_fixture_config('test') }
45
+
46
+ it "should change the log level" do
47
+ Appsignal.start
48
+ Appsignal.logger.level.should == Logger::DEBUG
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '.active?' do
54
+ subject { Appsignal.active? }
55
+
56
+ context "without config" do
57
+ before { Appsignal.stub(:config => nil) }
58
+
59
+ it { should be_false }
60
+ end
61
+
62
+ context "with config but inactive" do
63
+ before { Appsignal.stub(:config => {:active => false}) }
64
+
65
+ it { should be_false }
66
+ end
67
+
68
+ context "with active config" do
69
+ before { Appsignal.stub(:config => {:active => true}) }
70
+
71
+ it { should be_true }
72
+ end
73
+ end
74
+
75
+ context "with config and started" do
76
+ before do
77
+ Appsignal.config = project_fixture_config
78
+ Appsignal.start
79
+ end
80
+
81
+ describe ".enqueue" do
82
+ subject { Appsignal.enqueue(transaction) }
83
+
84
+ it "forwards the call to the agent" do
85
+ Appsignal.agent.should respond_to(:enqueue)
86
+ Appsignal.agent.should_receive(:enqueue).with(transaction)
87
+ subject
88
+ end
89
+ end
90
+
91
+ describe ".tag_request" do
92
+ before { Appsignal::Transaction.stub(:current => transaction) }
93
+
94
+ context "with transaction" do
95
+ let(:transaction) { double }
96
+ it "should call set_tags on transaction" do
97
+
98
+ transaction.should_receive(:set_tags).with({'a' => 'b'})
99
+ end
100
+
101
+ after { Appsignal.tag_request({'a' => 'b'}) }
102
+ end
103
+
104
+ context "without transaction" do
105
+ let(:transaction) { nil }
106
+
107
+ it "should call set_tags on transaction" do
108
+ Appsignal.tag_request.should be_false
109
+ end
110
+ end
111
+ end
112
+
113
+ describe ".transactions" do
114
+ subject { Appsignal.transactions }
115
+
116
+ it { should be_a Hash }
117
+ end
118
+
119
+ describe '.logger' do
120
+ subject { Appsignal.logger }
121
+
122
+ it { should be_a Logger }
123
+ end
124
+
125
+ describe '.config' do
126
+ subject { Appsignal.config }
127
+
128
+ it { should be_a Appsignal::Config }
129
+ it 'should return configuration' do
130
+ subject[:endpoint].should == 'https://push.appsignal.com/1'
131
+ end
132
+ end
133
+
134
+ describe ".json" do
135
+ subject { Appsignal.json }
136
+
137
+ it { should == ActiveSupport::JSON }
138
+ end
139
+
140
+ describe ".post_processing_middleware" do
141
+ before { Appsignal.instance_variable_set(:@post_processing_chain, nil) }
142
+
143
+ it "returns the default middleware stack" do
144
+ Appsignal::Aggregator::PostProcessor.should_receive(:default_middleware)
145
+ Appsignal.post_processing_middleware
146
+ end
147
+
148
+ it "returns a chain when called without a block" do
149
+ instance = Appsignal.post_processing_middleware
150
+ instance.should be_an_instance_of Appsignal::Aggregator::Middleware::Chain
151
+ end
152
+
153
+ context "when passing a block" do
154
+ it "yields an appsignal middleware chain" do
155
+ Appsignal.post_processing_middleware do |o|
156
+ o.should be_an_instance_of Appsignal::Aggregator::Middleware::Chain
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ describe ".send_exception" do
163
+ it "should send the exception to AppSignal" do
164
+ agent = double
165
+ Appsignal.stub(:agent).and_return(agent)
166
+ agent.should_receive(:send_queue)
167
+ agent.should_receive(:enqueue).with(kind_of(Appsignal::Transaction))
168
+
169
+ Appsignal::Transaction.should_receive(:create).and_call_original
170
+ end
171
+
172
+ it "should not send the exception if it's in the ignored list" do
173
+ Appsignal.stub(:is_ignored_exception? => true)
174
+ Appsignal::Transaction.should_not_receive(:create)
175
+ end
176
+
177
+ after do
178
+ begin
179
+ raise "I am an exception"
180
+ rescue Exception => e
181
+ Appsignal.send_exception(e)
182
+ end
183
+ end
184
+ end
185
+
186
+ describe ".listen_for_exception" do
187
+ it "should call send_exception and re-raise" do
188
+ Appsignal.should_receive(:send_exception).with(kind_of(Exception))
189
+ lambda {
190
+ Appsignal.listen_for_exception do
191
+ raise "I am an exception"
192
+ end
193
+ }.should raise_error(RuntimeError, "I am an exception")
194
+ end
195
+ end
196
+
197
+ describe ".add_exception" do
198
+ before { Appsignal::Transaction.stub(:current => transaction) }
199
+ let(:exception) { RuntimeError.new('I am an exception') }
200
+
201
+ it "should add the exception to the current transaction" do
202
+ transaction.should_receive(:add_exception).with(exception)
203
+
204
+ Appsignal.add_exception(exception)
205
+ end
206
+
207
+ it "should do nothing if there is no current transaction" do
208
+ Appsignal::Transaction.stub(:current => nil)
209
+
210
+ transaction.should_not_receive(:add_exception).with(exception)
211
+
212
+ Appsignal.add_exception(exception)
213
+ end
214
+
215
+ it "should not add the exception if it's in the ignored list" do
216
+ Appsignal.stub(:is_ignored_exception? => true)
217
+
218
+ transaction.should_not_receive(:add_exception).with(exception)
219
+
220
+ Appsignal.add_exception(exception)
221
+ end
222
+
223
+ it "should do nothing if the exception is nil" do
224
+ transaction.should_not_receive(:add_exception)
225
+
226
+ Appsignal.add_exception(nil)
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ if rails_present?
4
+ require 'generator_spec/test_case'
5
+ require './lib/generators/appsignal/appsignal_generator'
6
+
7
+ # The generator doesn't know we're testing
8
+ # So change the path while running the generator
9
+ # Change it back upon completion
10
+ def run_generator_in_tmp(args=[])
11
+ FileUtils.cd(tmp_dir) do
12
+ FileUtils.mkdir_p('config/environments')
13
+ FileUtils.touch('config/environments/development.rb')
14
+ FileUtils.touch('config/environments/production.rb')
15
+ @output = run_generator(args)
16
+ end
17
+ end
18
+
19
+ describe AppsignalGenerator do
20
+ include GeneratorSpec::TestCase
21
+ destination tmp_dir
22
+
23
+ before do
24
+ FileUtils.rm_rf(tmp_dir)
25
+ end
26
+
27
+ context "with key" do
28
+ context "known key" do
29
+ before do
30
+ authcheck = double
31
+ Appsignal::AuthCheck.should_receive(:new).and_return(authcheck)
32
+ authcheck.should_receive(:perform_with_result).and_return(['200', 'everything ok'])
33
+
34
+ prepare_destination
35
+ run_generator_in_tmp %w(my_app_key)
36
+ end
37
+
38
+ it "should generate a correct config file" do
39
+ fixture_config_file = File.open(File.join(fixtures_dir, 'generated_config.yml')).read
40
+ generated_config_file = File.open(File.join(tmp_dir, 'config/appsignal.yml')).read
41
+
42
+ generated_config_file.should == fixture_config_file
43
+ end
44
+
45
+ it "should mention successful auth check" do
46
+ @output.should include('success everything ok')
47
+ end
48
+ end
49
+
50
+ context "invalid key" do
51
+ before do
52
+ authcheck = double
53
+ Appsignal::AuthCheck.should_receive(:new).and_return(authcheck)
54
+ authcheck.should_receive(:perform_with_result).and_return(['401', 'unauthorized'])
55
+
56
+ prepare_destination
57
+ run_generator_in_tmp %w(my_app_key)
58
+ end
59
+
60
+ it "should mention invalid key" do
61
+ @output.should include('error unauthorized')
62
+ end
63
+ end
64
+
65
+ context "failed check" do
66
+ before do
67
+ authcheck = double
68
+ Appsignal::AuthCheck.should_receive(:new).and_return(authcheck)
69
+ authcheck.should_receive(:perform_with_result).and_return(['500', 'error!'])
70
+
71
+ prepare_destination
72
+ run_generator_in_tmp %w(my_app_key)
73
+ end
74
+
75
+ it "should mention failed check" do
76
+ @output.should include('error error!')
77
+ end
78
+ end
79
+
80
+ context "internal failure" do
81
+ before do
82
+ authcheck = Appsignal::AuthCheck.new(nil, nil)
83
+ authcheck.stub(:perform).and_throw(:error)
84
+ Appsignal::AuthCheck.should_receive(:new).and_return(authcheck)
85
+
86
+ prepare_destination
87
+ run_generator_in_tmp %w(my_app_key)
88
+ end
89
+
90
+ it "should mention internal failure" do
91
+ @output.should include(
92
+ 'Something went wrong while trying to '\
93
+ 'authenticate with AppSignal:'
94
+ )
95
+ end
96
+ end
97
+ end
98
+
99
+ context "without key" do
100
+ before do
101
+ prepare_destination
102
+ run_generator_in_tmp %w()
103
+ end
104
+
105
+ it "should not create a config file" do
106
+ destination_root.should have_structure {
107
+ directory 'config' do
108
+ no_file 'appsignal.yml'
109
+ no_file 'deploy.rb'
110
+ end
111
+ }
112
+ end
113
+ end
114
+
115
+ context "without capistrano" do
116
+ before do
117
+ prepare_destination
118
+ run_generator_in_tmp %w(my_app_key)
119
+ end
120
+
121
+ it "should create a config file" do
122
+ destination_root.should have_structure {
123
+ directory 'config' do
124
+ file 'appsignal.yml'
125
+ no_file 'deploy.rb'
126
+ end
127
+ }
128
+ end
129
+
130
+ it "should mention the deploy task" do
131
+ @output.should include('No capistrano setup detected!')
132
+ @output.should include('appsignal notify_of_deploy -h')
133
+ end
134
+ end
135
+
136
+ context "with capistrano" do
137
+ before do
138
+ cap_file = File.join(destination_root, 'Capfile')
139
+ deploy_file = File.join(destination_root, 'config', 'deploy.rb')
140
+
141
+ prepare_destination
142
+ File.open(cap_file, 'w') {}
143
+ FileUtils.mkdir(File.join(destination_root, 'config'))
144
+ File.open(deploy_file, 'w') {}
145
+ run_generator_in_tmp %w(my_app_key)
146
+ end
147
+
148
+ it "should create a config file and modify the capistrano deploy file" do
149
+ destination_root.should have_structure {
150
+ file 'Capfile'
151
+ directory 'config' do
152
+ file 'appsignal.yml'
153
+ file 'deploy.rb' do
154
+ contains "require 'appsignal/capistrano'"
155
+ end
156
+ end
157
+ }
158
+ end
159
+
160
+ it "should not mention the deploy task" do
161
+ @output.should_not include('No capistrano setup detected!')
162
+ @output.should_not include('appsignal notify_of_deploy -h')
163
+ end
164
+ end
165
+ end
166
+ end