appsignal 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +19 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +30 -0
  4. data/Gemfile +3 -0
  5. data/LICENCE +20 -0
  6. data/README.md +48 -0
  7. data/Rakefile +52 -0
  8. data/appsignal.gemspec +33 -0
  9. data/bin/appsignal +13 -0
  10. data/config/appsignal.yml +8 -0
  11. data/gemfiles/3.0.gemfile +16 -0
  12. data/gemfiles/3.1.gemfile +16 -0
  13. data/gemfiles/3.2.gemfile +16 -0
  14. data/gemfiles/edge.gemfile +16 -0
  15. data/lib/appsignal.rb +45 -0
  16. data/lib/appsignal/agent.rb +104 -0
  17. data/lib/appsignal/auth_check.rb +19 -0
  18. data/lib/appsignal/capistrano.rb +41 -0
  19. data/lib/appsignal/cli.rb +118 -0
  20. data/lib/appsignal/config.rb +30 -0
  21. data/lib/appsignal/exception_notification.rb +25 -0
  22. data/lib/appsignal/marker.rb +35 -0
  23. data/lib/appsignal/middleware.rb +30 -0
  24. data/lib/appsignal/railtie.rb +19 -0
  25. data/lib/appsignal/transaction.rb +77 -0
  26. data/lib/appsignal/transaction/faulty_request_formatter.rb +30 -0
  27. data/lib/appsignal/transaction/params_sanitizer.rb +36 -0
  28. data/lib/appsignal/transaction/regular_request_formatter.rb +11 -0
  29. data/lib/appsignal/transaction/slow_request_formatter.rb +34 -0
  30. data/lib/appsignal/transaction/transaction_formatter.rb +93 -0
  31. data/lib/appsignal/transmitter.rb +53 -0
  32. data/lib/appsignal/version.rb +3 -0
  33. data/lib/generators/appsignal/USAGE +8 -0
  34. data/lib/generators/appsignal/appsignal_generator.rb +70 -0
  35. data/lib/generators/appsignal/templates/appsignal.yml +4 -0
  36. data/log/.gitkeep +0 -0
  37. data/resources/cacert.pem +3849 -0
  38. data/spec/appsignal/agent_spec.rb +259 -0
  39. data/spec/appsignal/auth_check_spec.rb +36 -0
  40. data/spec/appsignal/capistrano_spec.rb +81 -0
  41. data/spec/appsignal/cli_spec.rb +124 -0
  42. data/spec/appsignal/config_spec.rb +40 -0
  43. data/spec/appsignal/exception_notification_spec.rb +12 -0
  44. data/spec/appsignal/inactive_railtie_spec.rb +30 -0
  45. data/spec/appsignal/marker_spec.rb +83 -0
  46. data/spec/appsignal/middleware_spec.rb +73 -0
  47. data/spec/appsignal/railtie_spec.rb +54 -0
  48. data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +49 -0
  49. data/spec/appsignal/transaction/params_sanitizer_spec.rb +68 -0
  50. data/spec/appsignal/transaction/regular_request_formatter_spec.rb +14 -0
  51. data/spec/appsignal/transaction/slow_request_formatter_spec.rb +76 -0
  52. data/spec/appsignal/transaction/transaction_formatter_spec.rb +178 -0
  53. data/spec/appsignal/transaction_spec.rb +191 -0
  54. data/spec/appsignal/transmitter_spec.rb +64 -0
  55. data/spec/appsignal_spec.rb +66 -0
  56. data/spec/generators/appsignal/appsignal_generator_spec.rb +222 -0
  57. data/spec/spec_helper.rb +85 -0
  58. data/spec/support/delegate_matcher.rb +39 -0
  59. metadata +247 -0
@@ -0,0 +1,259 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::Agent do
4
+ let(:transaction) { stub(
5
+ :name => 'transaction',
6
+ :exception? => false,
7
+ :action => 'something#else'
8
+ ) }
9
+
10
+ describe '#add_to_queue' do
11
+ before do
12
+ @agent = Appsignal::Agent.new
13
+ @exception_transaction = stub(
14
+ :name => 'exception',
15
+ :exception? => true,
16
+ :action => 'controller#action1'
17
+ )
18
+ @slow_transaction = stub(
19
+ :name => 'slow',
20
+ :action => 'controller#action1',
21
+ :exception? => false,
22
+ :process_action_event => stub(
23
+ :duration => 250.0
24
+ )
25
+ )
26
+ @slower_transaction = stub(
27
+ :name => 'slow',
28
+ :action => 'controller#action1',
29
+ :exception? => false,
30
+ :process_action_event => stub(
31
+ :duration => 300.0
32
+ )
33
+ )
34
+ @other_slow_transaction = stub(
35
+ :name => 'slow',
36
+ :action => 'controller#action1',
37
+ :exception? => false,
38
+ :process_action_event => stub(
39
+ :duration => 260.0
40
+ )
41
+ )
42
+ @slow_transaction_in_other_action = stub(
43
+ :name => 'slow',
44
+ :action => 'controller#action2',
45
+ :exception? => false,
46
+ :process_action_event => stub(
47
+ :duration => 400.0
48
+ )
49
+ )
50
+ end
51
+ subject { @agent }
52
+
53
+ context "an exception transaction" do
54
+ before do
55
+ @exception_transaction.should_not_receive(:clear_payload_and_events!)
56
+ subject.add_to_queue(@exception_transaction)
57
+ end
58
+
59
+ its(:queue) { should include(@exception_transaction) }
60
+ its(:slowest_transactions) { should be_empty }
61
+
62
+ context "a slow transaction" do
63
+ before do
64
+ subject.add_to_queue(@slow_transaction)
65
+ end
66
+
67
+ its(:queue) { should include(@slow_transaction) }
68
+ its(:slowest_transactions) { should == {
69
+ 'controller#action1' => @slow_transaction
70
+ } }
71
+
72
+ context "a slower transaction in the same action" do
73
+ before do
74
+ @slow_transaction.should_receive(:clear_payload_and_events!)
75
+ @slower_transaction.should_not_receive(:clear_payload_and_events!)
76
+ subject.add_to_queue(@slower_transaction)
77
+ end
78
+
79
+ its(:queue) { should include(@slower_transaction) }
80
+ its(:slowest_transactions) { should == {
81
+ 'controller#action1' => @slower_transaction
82
+ } }
83
+
84
+ context "a slow but not the slowest transaction in the same action" do
85
+ before do
86
+ @other_slow_transaction.should_receive(:clear_payload_and_events!)
87
+ subject.add_to_queue(@other_slow_transaction)
88
+ end
89
+
90
+ its(:queue) { should include(@other_slow_transaction) }
91
+ its(:slowest_transactions) { should == {
92
+ 'controller#action1' => @slower_transaction
93
+ } }
94
+ end
95
+
96
+ context "an even slower transaction in a different action" do
97
+ before do
98
+ @slow_transaction_in_other_action.should_not_receive(:clear_payload_and_events!)
99
+ subject.add_to_queue(@slow_transaction_in_other_action)
100
+ end
101
+
102
+ its(:queue) { should include(@slow_transaction_in_other_action) }
103
+ its(:slowest_transactions) { should == {
104
+ 'controller#action1' => @slower_transaction,
105
+ 'controller#action2' => @slow_transaction_in_other_action
106
+ } }
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ describe "#send_queue" do
114
+ it "transmits" do
115
+ subject.stub(:queue => [stub(:to_hash => 'foo')])
116
+ subject.transmitter.should_receive(:transmit).with(['foo'])
117
+ end
118
+
119
+ it "handles the return code" do
120
+ subject.transmitter.stub(:transmit => '200')
121
+ subject.should_receive(:handle_result).with('200')
122
+ end
123
+
124
+ it "handles exceptions in transmit" do
125
+ subject.transmitter.stub(:transmit).and_raise(Exception.new)
126
+ subject.should_receive(:handle_result).with(nil)
127
+ Appsignal.logger.should_receive(:error).with('Exception while communicating with AppSignal: Exception')
128
+ end
129
+
130
+ after { subject.send_queue }
131
+ end
132
+
133
+ describe '#handle_result' do
134
+ before { subject.add_to_queue(transaction) }
135
+ before { subject.instance_variable_set(:@sleep_time, 3.0) }
136
+
137
+ context "good responses" do
138
+ before { subject.handle_result(code) }
139
+
140
+ context "with 200" do
141
+ let(:code) { '200' }
142
+
143
+ its(:queue) { should be_empty }
144
+ its(:slowest_transactions) { should be_empty }
145
+ end
146
+
147
+ context "with 420" do
148
+ let(:code) { '420' }
149
+
150
+ its(:queue) { should be_empty }
151
+ its(:slowest_transactions) { should be_empty }
152
+ its(:sleep_time) { should == 4.5 }
153
+ end
154
+
155
+ context "with 413" do
156
+ let(:code) { '413' }
157
+
158
+ its(:queue) { should be_empty }
159
+ its(:slowest_transactions) { should be_empty }
160
+ its(:sleep_time) { should == 2.0 }
161
+ end
162
+ end
163
+
164
+ context "bad responses" do
165
+ context "with 429" do
166
+ let(:code) { '429' }
167
+
168
+ it "calls a stop to logging" do
169
+ subject.should_receive :stop_logging
170
+ end
171
+ end
172
+
173
+ context "with 406" do
174
+ let(:code) { '406' }
175
+
176
+ it "calls a stop to logging" do
177
+ subject.should_receive :stop_logging
178
+ end
179
+ end
180
+
181
+ context "with 402" do
182
+ let(:code) { '402' }
183
+
184
+ it "calls a stop to logging" do
185
+ subject.should_receive :stop_logging
186
+ end
187
+ end
188
+
189
+ context "with 401" do
190
+ let(:code) { '401' }
191
+
192
+ it "calls a stop to logging" do
193
+ subject.should_receive :stop_logging
194
+ end
195
+ end
196
+
197
+ context "any other response" do
198
+ let(:code) { 'any other response' }
199
+
200
+ it "calls retry_once" do
201
+ subject.should_receive :retry_once
202
+ end
203
+ end
204
+
205
+ after { subject.handle_result(code) }
206
+ end
207
+ end
208
+
209
+ describe "#good_response" do
210
+ before do
211
+ subject.instance_variable_set(:@retry_once, false)
212
+ subject.add_to_queue(transaction)
213
+ subject.send :good_response
214
+ end
215
+
216
+ its(:queue) { should be_empty }
217
+ its(:slowest_transactions) { should be_empty }
218
+
219
+ it "allows the next request to be retried" do
220
+ subject.instance_variable_get(:@retry_request).should be_true
221
+ end
222
+ end
223
+
224
+ describe "#retry_once" do
225
+ before do
226
+ subject.add_to_queue(transaction)
227
+ subject.send :retry_once
228
+ end
229
+
230
+ context "on time," do
231
+ its(:queue) { should == [transaction] }
232
+ its(:slowest_transactions) { should == {
233
+ 'something#else' => transaction
234
+ } }
235
+
236
+ context "two times" do
237
+ before { subject.send :retry_once }
238
+
239
+ its(:queue) { should be_empty }
240
+ end
241
+ end
242
+ end
243
+
244
+ describe "#stop_logging" do
245
+ it "does not raise exceptions" do
246
+ expect { subject.send :stop_logging }.not_to raise_error
247
+ end
248
+ end
249
+
250
+ describe "when inactive" do
251
+ before { Appsignal.stub(:active? => false) }
252
+
253
+ it "should not start a new thread" do
254
+ Thread.should_not_receive(:new)
255
+ end
256
+
257
+ after { Appsignal::Agent.new }
258
+ end
259
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::AuthCheck do
4
+ let(:auth_check) { Appsignal::AuthCheck.new('production') }
5
+ before do
6
+ @transmitter = mock
7
+ Appsignal::Transmitter.should_receive(:new).
8
+ with('http://localhost:3000/1', 'auth', 'abc').
9
+ and_return(@transmitter)
10
+ end
11
+
12
+ describe "#perform" do
13
+ it "should not transmit any extra data" do
14
+ @transmitter.should_receive(:transmit).with({}).and_return({})
15
+ auth_check.perform
16
+ end
17
+ end
18
+
19
+ describe "#uri" do
20
+ before do
21
+ @transmitter.should_receive(:transmit)
22
+ auth_check.perform
23
+ end
24
+
25
+ it "should delegate to transmitter" do
26
+ @transmitter.should_receive(:uri)
27
+ auth_check.uri
28
+ end
29
+
30
+ it "should return uri" do
31
+ @transmitter.should_receive(:uri).
32
+ and_return('http://appsignal.com/1/auth')
33
+ auth_check.uri.should == 'http://appsignal.com/1/auth'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'appsignal/capistrano'
3
+ require 'capistrano/configuration'
4
+
5
+ describe Appsignal::Capistrano do
6
+ before :all do
7
+ @config = Capistrano::Configuration.new
8
+ Appsignal::Capistrano.tasks(@config)
9
+ end
10
+
11
+ it "should have a deploy task" do
12
+ @config.find_task('appsignal:deploy').should_not be_nil
13
+ end
14
+
15
+ describe "appsignal:deploy task" do
16
+ before :all do
17
+ @config.set(:rails_env, 'development')
18
+ @config.set(:repository, 'master')
19
+ @config.set(:deploy_to, '/home/username/app')
20
+ @config.set(:current_release, '')
21
+ @config.set(:current_revision, '503ce0923ed177a3ce000005')
22
+ ENV['USER'] = 'batman'
23
+ end
24
+
25
+ context "send marker" do
26
+ let(:marker_data) {
27
+ {
28
+ :revision => "503ce0923ed177a3ce000005",
29
+ :repository => "master",
30
+ :user => "batman"
31
+ }
32
+ }
33
+ before :all do
34
+ @io = StringIO.new
35
+ @logger = Capistrano::Logger.new(:output => @io)
36
+ @logger.level = Capistrano::Logger::MAX_LEVEL
37
+ @config.logger = @logger
38
+ end
39
+ before do
40
+ @marker = Appsignal::Marker.new(marker_data, Rails.root.to_s,
41
+ 'development', @logger)
42
+ Appsignal::Marker.should_receive(:new).
43
+ with(marker_data, Rails.root.to_s, 'development', anything()).
44
+ and_return(@marker)
45
+ end
46
+
47
+ context "proper setup" do
48
+ before do
49
+ @transmitter = mock()
50
+ Appsignal::Transmitter.should_receive(:new).and_return(@transmitter)
51
+ end
52
+
53
+ it "should transmit data" do
54
+ @transmitter.should_receive(:transmit).and_return('200')
55
+ @config.find_and_execute_task('appsignal:deploy')
56
+ @io.string.should include('** Notifying Appsignal of deploy...')
57
+ @io.string.should include('** Appsignal has been notified of this '\
58
+ 'deploy!')
59
+ end
60
+ end
61
+
62
+ it "should not transmit data" do
63
+ @config.find_and_execute_task('appsignal:deploy')
64
+ @io.string.should include('** Notifying Appsignal of deploy...')
65
+ @io.string.should include('** Something went wrong while trying to '\
66
+ 'notify Appsignal:')
67
+ end
68
+
69
+ context "dry run" do
70
+ before(:all) { @config.dry_run = true }
71
+
72
+ it "should not send deploy marker" do
73
+ @marker.should_not_receive(:transmit)
74
+ @config.find_and_execute_task('appsignal:deploy')
75
+ @io.string.should include('** Dry run: Deploy marker not actually '\
76
+ 'sent.')
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Appsignal::CLI do
4
+ let(:out_stream) { StringIO.new }
5
+ let(:error_stream) { StringIO.new }
6
+ let(:cli) { Appsignal::CLI }
7
+ before :each do
8
+ $stdout, $stderr = out_stream, error_stream
9
+ end
10
+
11
+ describe "#logger" do
12
+ it "should be a logger" do
13
+ cli.logger.should be_instance_of(Logger)
14
+ end
15
+ end
16
+
17
+ it "should print a message if there is no config file" do
18
+ File.stub(:exists? => false)
19
+ lambda {
20
+ cli.run([])
21
+ }.should raise_error(SystemExit)
22
+ out_stream.string.should include 'No config file present at config/appsignal.yml'
23
+ out_stream.string.should include 'Log in to https://appsignal.com to get instructions on how to generate the config file.'
24
+ end
25
+
26
+ it "should print the help with no arguments, -h and --help" do
27
+ [nil, '-h', '--help'].each do |arg|
28
+ lambda {
29
+ cli.run([arg].compact)
30
+ }.should raise_error(SystemExit)
31
+
32
+ out_stream.string.should include 'appsignal <command> [options]'
33
+ out_stream.string.should include 'Available commands: notify_of_deploy'
34
+ end
35
+ end
36
+
37
+ it "should print the version with -v and --version" do
38
+ ['-v', '--version'].each do |arg|
39
+ lambda {
40
+ cli.run([arg])
41
+ }.should raise_error(SystemExit)
42
+
43
+ out_stream.string.should include 'Appsignal'
44
+ out_stream.string.should include '.'
45
+ end
46
+ end
47
+
48
+ it "should print a notice if a command does not exist" do
49
+ lambda {
50
+ cli.run(['nonsense'])
51
+ }.should raise_error(SystemExit)
52
+
53
+ out_stream.string.should include "Command 'nonsense' does not exist, run appsignal -h to see the help"
54
+ end
55
+
56
+ describe "#validate_required_options" do
57
+ let(:required_options) { [:option_1, :option_2, :option_3] }
58
+
59
+ it "should do nothing with all options supplied" do
60
+ cli.validate_required_options(
61
+ required_options,
62
+ :option_1 => 1,
63
+ :option_2 => 2,
64
+ :option_3 => 3
65
+ )
66
+ out_stream.string.should be_empty
67
+ end
68
+
69
+ it "should print a message with one option missing" do
70
+ lambda {
71
+ cli.validate_required_options(
72
+ required_options,
73
+ :option_1 => 1,
74
+ :option_2 => 2
75
+ )
76
+ }.should raise_error(SystemExit)
77
+ out_stream.string.should include("Missing options: option_3")
78
+ end
79
+
80
+ it "should print a message with multiple options missing" do
81
+ lambda {
82
+ cli.validate_required_options(
83
+ required_options,
84
+ :option_1 => 1,
85
+ :option_2 => ''
86
+ )
87
+ }.should raise_error(SystemExit)
88
+ out_stream.string.should include("Missing options: option_2, option_3")
89
+ end
90
+ end
91
+
92
+ describe "notify_of_deploy" do
93
+ it "should validate that all options have been supplied" do
94
+ options = {}
95
+ cli.should_receive(:validate_required_options).with(
96
+ [:revision, :repository, :user, :environment],
97
+ options
98
+ )
99
+ cli.notify_of_deploy(options)
100
+ end
101
+
102
+ it "should notify of a deploy" do
103
+ transmitter = double
104
+ Appsignal::Transmitter.should_receive(:new).with(
105
+ 'http://localhost:3000/1',
106
+ 'markers',
107
+ 'abc'
108
+ ).and_return(transmitter)
109
+ transmitter.should_receive(:transmit).with(
110
+ :revision => 'aaaaa',
111
+ :repository => 'git@github.com:our/project.git',
112
+ :user => 'thijs'
113
+ )
114
+
115
+ cli.run([
116
+ 'notify_of_deploy',
117
+ '--revision=aaaaa',
118
+ '--repository=git@github.com:our/project.git',
119
+ '--user=thijs',
120
+ '--environment=production'
121
+ ])
122
+ end
123
+ end
124
+ end