appsignal 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rvmrc +1 -0
- data/.travis.yml +30 -0
- data/Gemfile +3 -0
- data/LICENCE +20 -0
- data/README.md +48 -0
- data/Rakefile +52 -0
- data/appsignal.gemspec +33 -0
- data/bin/appsignal +13 -0
- data/config/appsignal.yml +8 -0
- data/gemfiles/3.0.gemfile +16 -0
- data/gemfiles/3.1.gemfile +16 -0
- data/gemfiles/3.2.gemfile +16 -0
- data/gemfiles/edge.gemfile +16 -0
- data/lib/appsignal.rb +45 -0
- data/lib/appsignal/agent.rb +104 -0
- data/lib/appsignal/auth_check.rb +19 -0
- data/lib/appsignal/capistrano.rb +41 -0
- data/lib/appsignal/cli.rb +118 -0
- data/lib/appsignal/config.rb +30 -0
- data/lib/appsignal/exception_notification.rb +25 -0
- data/lib/appsignal/marker.rb +35 -0
- data/lib/appsignal/middleware.rb +30 -0
- data/lib/appsignal/railtie.rb +19 -0
- data/lib/appsignal/transaction.rb +77 -0
- data/lib/appsignal/transaction/faulty_request_formatter.rb +30 -0
- data/lib/appsignal/transaction/params_sanitizer.rb +36 -0
- data/lib/appsignal/transaction/regular_request_formatter.rb +11 -0
- data/lib/appsignal/transaction/slow_request_formatter.rb +34 -0
- data/lib/appsignal/transaction/transaction_formatter.rb +93 -0
- data/lib/appsignal/transmitter.rb +53 -0
- data/lib/appsignal/version.rb +3 -0
- data/lib/generators/appsignal/USAGE +8 -0
- data/lib/generators/appsignal/appsignal_generator.rb +70 -0
- data/lib/generators/appsignal/templates/appsignal.yml +4 -0
- data/log/.gitkeep +0 -0
- data/resources/cacert.pem +3849 -0
- data/spec/appsignal/agent_spec.rb +259 -0
- data/spec/appsignal/auth_check_spec.rb +36 -0
- data/spec/appsignal/capistrano_spec.rb +81 -0
- data/spec/appsignal/cli_spec.rb +124 -0
- data/spec/appsignal/config_spec.rb +40 -0
- data/spec/appsignal/exception_notification_spec.rb +12 -0
- data/spec/appsignal/inactive_railtie_spec.rb +30 -0
- data/spec/appsignal/marker_spec.rb +83 -0
- data/spec/appsignal/middleware_spec.rb +73 -0
- data/spec/appsignal/railtie_spec.rb +54 -0
- data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +49 -0
- data/spec/appsignal/transaction/params_sanitizer_spec.rb +68 -0
- data/spec/appsignal/transaction/regular_request_formatter_spec.rb +14 -0
- data/spec/appsignal/transaction/slow_request_formatter_spec.rb +76 -0
- data/spec/appsignal/transaction/transaction_formatter_spec.rb +178 -0
- data/spec/appsignal/transaction_spec.rb +191 -0
- data/spec/appsignal/transmitter_spec.rb +64 -0
- data/spec/appsignal_spec.rb +66 -0
- data/spec/generators/appsignal/appsignal_generator_spec.rb +222 -0
- data/spec/spec_helper.rb +85 -0
- data/spec/support/delegate_matcher.rb +39 -0
- 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
|