appsignal 0.4.0

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