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.
- 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,178 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::TransactionFormatter do
|
4
|
+
let(:klass) { Appsignal::TransactionFormatter }
|
5
|
+
let(:formatter) { klass.new(transaction) }
|
6
|
+
let(:transaction) do
|
7
|
+
Appsignal::Transaction.create('1', {
|
8
|
+
'HTTP_USER_AGENT' => 'IE6',
|
9
|
+
'SERVER_NAME' => 'localhost',
|
10
|
+
'action_dispatch.routes' => 'not_available'
|
11
|
+
})
|
12
|
+
end
|
13
|
+
subject { formatter }
|
14
|
+
|
15
|
+
describe ".regular" do
|
16
|
+
subject { klass.regular(transaction) }
|
17
|
+
it { should be_a klass::RegularRequestFormatter }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".slow" do
|
21
|
+
subject { klass.slow(transaction) }
|
22
|
+
it { klass.slow(transaction).should be_a klass::SlowRequestFormatter }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".faulty" do
|
26
|
+
subject { klass.faulty(transaction) }
|
27
|
+
it { should be_a klass::FaultyRequestFormatter }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "a new formatter" do
|
31
|
+
describe "#to_hash" do
|
32
|
+
subject { formatter.to_hash }
|
33
|
+
before { formatter.stub(:action => :foo, :formatted_process_action_event => :bar) }
|
34
|
+
|
35
|
+
it 'returns a formatted hash of the transaction data' do
|
36
|
+
should == {
|
37
|
+
:request_id => '1',
|
38
|
+
:action => :foo,
|
39
|
+
:log_entry => :bar,
|
40
|
+
:failed => false
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# protected
|
47
|
+
|
48
|
+
it { should delegate(:id).to(:transaction) }
|
49
|
+
it { should delegate(:events).to(:transaction) }
|
50
|
+
it { should delegate(:exception).to(:transaction) }
|
51
|
+
it { should delegate(:exception?).to(:transaction) }
|
52
|
+
it { should delegate(:env).to(:transaction) }
|
53
|
+
it { should delegate(:request).to(:transaction) }
|
54
|
+
it { should delegate(:process_action_event).to(:transaction) }
|
55
|
+
it { should delegate(:action).to(:transaction) }
|
56
|
+
|
57
|
+
it { should delegate(:payload).to(:process_action_event) }
|
58
|
+
|
59
|
+
context "a new formatter" do
|
60
|
+
describe "#formatted_process_action_event" do
|
61
|
+
subject { formatter.send(:formatted_process_action_event) }
|
62
|
+
|
63
|
+
it "calls basic_process_action_event" do
|
64
|
+
formatter.should_receive(:basic_process_action_event)
|
65
|
+
subject
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with actual process action event data" do
|
69
|
+
before { transaction.set_process_action_event(create_process_action_event) }
|
70
|
+
|
71
|
+
it { should be_a Hash }
|
72
|
+
|
73
|
+
it "merges formatted_payload on the basic_process_action_event" do
|
74
|
+
subject[:duration].should == 1000.0
|
75
|
+
subject[:action].should == 'BlogPostsController#show'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "without any process action event data" do
|
80
|
+
it { should be_a Hash }
|
81
|
+
|
82
|
+
it "does not merge formatted_payload onto the basic_process_action_event" do
|
83
|
+
subject.keys.should_not include :duration
|
84
|
+
subject.keys.should_not include :action
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#basic_process_action_event" do
|
90
|
+
subject { formatter.send(:basic_process_action_event) }
|
91
|
+
before do
|
92
|
+
transaction.stub(:request => mock(
|
93
|
+
:fullpath => '/blog',
|
94
|
+
:session => {:current_user => 1})
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
it { should == {
|
99
|
+
:path => '/blog',
|
100
|
+
:kind => 'http_request'
|
101
|
+
} }
|
102
|
+
|
103
|
+
it "has no environment key" do
|
104
|
+
subject[:environment].should be_nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it "has no session_data key" do
|
108
|
+
subject[:session_data].should be_nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#formatted_payload" do
|
113
|
+
let(:start_time) { Time.at(2.71828182) }
|
114
|
+
let(:end_time) { Time.at(3.141592654) }
|
115
|
+
subject { formatter.send(:formatted_payload) }
|
116
|
+
before do
|
117
|
+
transaction.stub(:sanitized_event_payload => {})
|
118
|
+
transaction.set_process_action_event(mock(
|
119
|
+
:name => 'name',
|
120
|
+
:duration => 2,
|
121
|
+
:time => start_time,
|
122
|
+
:end => end_time,
|
123
|
+
:payload => {
|
124
|
+
:controller => 'controller',
|
125
|
+
:action => 'action'
|
126
|
+
}
|
127
|
+
))
|
128
|
+
end
|
129
|
+
|
130
|
+
it { should == {
|
131
|
+
:action => 'controller#action',
|
132
|
+
:controller => 'controller', # DEBUG this should no longer be here now
|
133
|
+
:duration => 2,
|
134
|
+
:time => start_time.to_f,
|
135
|
+
:end => end_time.to_f
|
136
|
+
} }
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#sanitized_event_payload" do
|
140
|
+
subject do
|
141
|
+
formatter.send(:sanitized_event_payload, double(:payload => {:key => :sensitive}))
|
142
|
+
end
|
143
|
+
|
144
|
+
it "calls Appsignal event payload sanitizer" do
|
145
|
+
Appsignal.should_receive(:event_payload_sanitizer).and_return(
|
146
|
+
proc do |event|
|
147
|
+
event.payload[:key] = 'censored'
|
148
|
+
event.payload
|
149
|
+
end
|
150
|
+
)
|
151
|
+
subject.should == {:key => 'censored'}
|
152
|
+
end
|
153
|
+
|
154
|
+
it "calls params sanitizer" do
|
155
|
+
Appsignal::ParamsSanitizer.should_receive(:sanitize).and_return(
|
156
|
+
:key => 'sensitive'
|
157
|
+
)
|
158
|
+
subject.should == {:key => 'sensitive'}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#filtered_environment" do
|
163
|
+
subject { formatter.send(:filtered_environment) }
|
164
|
+
|
165
|
+
it "should have a SERVER_NAME" do
|
166
|
+
subject['SERVER_NAME'].should == 'localhost'
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should have a HTTP_USER_AGENT" do
|
170
|
+
subject['HTTP_USER_AGENT'].should == 'IE6'
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should not have a action_dispatch.routes" do
|
174
|
+
subject.should_not have_key 'action_dispatch.routes'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Transaction do
|
4
|
+
describe '.create' do
|
5
|
+
before { Appsignal::Transaction.create('1', {}) }
|
6
|
+
|
7
|
+
it 'should add the id to the thread' do
|
8
|
+
Thread.current[:appsignal_transaction_id].should == '1'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should add the transaction to the list' do
|
12
|
+
Appsignal.transactions['1'].should be_a Appsignal::Transaction
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.current' do
|
17
|
+
let(:transaction) { Appsignal::Transaction.create('1', {}) }
|
18
|
+
before { transaction }
|
19
|
+
subject { Appsignal::Transaction.current }
|
20
|
+
|
21
|
+
it 'should return the correct transaction' do
|
22
|
+
should eq transaction
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'transaction instance' do
|
27
|
+
let(:transaction) do
|
28
|
+
Appsignal::Transaction.create('1', {
|
29
|
+
'HTTP_USER_AGENT' => 'IE6',
|
30
|
+
'SERVER_NAME' => 'localhost',
|
31
|
+
'action_dispatch.routes' => 'not_available'
|
32
|
+
})
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#request' do
|
36
|
+
subject { transaction.request }
|
37
|
+
|
38
|
+
it { should be_a ActionDispatch::Request }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#set_process_action_event' do
|
42
|
+
let(:process_action_event) { create_process_action_event }
|
43
|
+
|
44
|
+
it 'should add a process action event' do
|
45
|
+
transaction.set_process_action_event(process_action_event)
|
46
|
+
|
47
|
+
transaction.process_action_event.should == process_action_event
|
48
|
+
transaction.action.should == 'BlogPostsController#show'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#add_event' do
|
53
|
+
let(:event) {stub(:name => 'test') }
|
54
|
+
|
55
|
+
it 'should add an event' do
|
56
|
+
expect {
|
57
|
+
transaction.add_event(event)
|
58
|
+
}.to change(transaction, :events).to([event])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#add_exception' do
|
63
|
+
let(:exception) {stub(:name => 'test') }
|
64
|
+
|
65
|
+
it 'should add an exception' do
|
66
|
+
expect {
|
67
|
+
transaction.add_exception(exception)
|
68
|
+
}.to change(transaction, :exception).to(exception)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#slow_request?' do
|
73
|
+
let(:duration) { 199 }
|
74
|
+
subject { transaction.slow_request? }
|
75
|
+
before { transaction.set_process_action_event(
|
76
|
+
stub(:duration => duration, :payload => {})
|
77
|
+
) }
|
78
|
+
|
79
|
+
it { should be_false }
|
80
|
+
|
81
|
+
context "when the request took too long" do
|
82
|
+
let(:duration) { 200 }
|
83
|
+
|
84
|
+
it { should be_true }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when process action event is empty" do
|
88
|
+
before { transaction.set_process_action_event(nil) }
|
89
|
+
|
90
|
+
it { should be_false }
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when process action event does not have a payload" do
|
94
|
+
before { transaction.set_process_action_event(stub(:payload => nil)) }
|
95
|
+
|
96
|
+
it { should be_false }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#clear_payload_and_events!" do
|
101
|
+
subject { slow_transaction }
|
102
|
+
|
103
|
+
it "should clear the process action payload and events" do
|
104
|
+
subject.clear_payload_and_events!
|
105
|
+
|
106
|
+
subject.process_action_event.payload.should be_empty
|
107
|
+
subject.events.should be_empty
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#to_hash' do
|
112
|
+
subject { transaction.to_hash }
|
113
|
+
before { transaction.stub(:exception? => false) }
|
114
|
+
|
115
|
+
context "with an exception request" do
|
116
|
+
before { transaction.stub(:exception? => true) }
|
117
|
+
|
118
|
+
it "calls TransactionFormatter.faulty with self" do
|
119
|
+
Appsignal::TransactionFormatter.should_receive(:faulty).
|
120
|
+
with(transaction).and_return({})
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "with a slow request" do
|
125
|
+
before { transaction.stub(:slow_request? => true) }
|
126
|
+
|
127
|
+
it "calls TransactionFormatter.slow with self" do
|
128
|
+
Appsignal::TransactionFormatter.should_receive(:slow).
|
129
|
+
with(transaction).and_return({})
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "with a regular request" do
|
134
|
+
before { transaction.stub(:slow_request? => false) }
|
135
|
+
|
136
|
+
it "calls TransactionFormatter.slow with self" do
|
137
|
+
Appsignal::TransactionFormatter.should_receive(:regular).
|
138
|
+
with(transaction).and_return({})
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
after { subject }
|
143
|
+
end
|
144
|
+
|
145
|
+
describe '#complete!' do
|
146
|
+
before { transaction.set_process_action_event(
|
147
|
+
stub(:duration => 199, :time => Time.now, :payload => nil)
|
148
|
+
) }
|
149
|
+
|
150
|
+
it 'should remove transaction from the list' do
|
151
|
+
expect {
|
152
|
+
transaction.complete!
|
153
|
+
}.to change(Appsignal.transactions, :length).by(-1)
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'calling the appsignal agent' do
|
157
|
+
context 'without events and exception' do
|
158
|
+
it 'should add transaction to the agent' do
|
159
|
+
Appsignal.agent.should_receive(:add_to_queue).with(transaction)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'with events' do
|
164
|
+
before { transaction.add_event(stub) }
|
165
|
+
|
166
|
+
it 'should add transaction to the agent' do
|
167
|
+
Appsignal.agent.should_receive(:add_to_queue).with(transaction)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'with exception' do
|
172
|
+
before { transaction.add_exception(stub) }
|
173
|
+
|
174
|
+
it 'should add transaction to the agent' do
|
175
|
+
Appsignal.agent.should_receive(:add_to_queue).with(transaction)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
after { transaction.complete! }
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'thread' do
|
183
|
+
before { transaction.complete! }
|
184
|
+
|
185
|
+
it 'should reset the thread transaction id' do
|
186
|
+
Thread.current[:appsignal_transaction_id].should be_nil
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Transmitter do
|
4
|
+
let(:_80beans_) { 'http://www.80beans.com' }
|
5
|
+
let(:action) { 'action' }
|
6
|
+
let(:klass) { Appsignal::Transmitter }
|
7
|
+
let(:instance) { klass.new(_80beans_, action, :the_api_key) }
|
8
|
+
subject { instance }
|
9
|
+
|
10
|
+
describe "#uri" do
|
11
|
+
it "returns the uri" do
|
12
|
+
Socket.stub(:gethostname => 'app1.local')
|
13
|
+
subject.uri.should ==
|
14
|
+
URI("http://www.80beans.com/action?api_key=the_api_key&hostname=app1.local&gem_version=#{Appsignal::VERSION}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#transmit" do
|
19
|
+
let(:http_client) { stub(:request => stub(:code => '200')) }
|
20
|
+
before { instance.stub(:http_client => http_client) }
|
21
|
+
|
22
|
+
subject { instance.transmit(:shipit => :payload) }
|
23
|
+
|
24
|
+
it { should == '200' }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#http_post" do
|
28
|
+
it "calls Net::HTTP.post_form with the correct params" do
|
29
|
+
post = stub
|
30
|
+
post.should_receive(:body=).with("{\"the\":\"payload\"}")
|
31
|
+
Socket.stub(:gethostname => 'app1.local')
|
32
|
+
|
33
|
+
Net::HTTP::Post.should_receive(:new).with(
|
34
|
+
"/action?api_key=the_api_key&hostname=app1.local&gem_version=#{Appsignal::VERSION}"
|
35
|
+
).and_return(post)
|
36
|
+
instance.send(:http_post, :the => :payload)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "ca_file_path" do
|
41
|
+
subject { instance.send(:ca_file_path) }
|
42
|
+
|
43
|
+
it { should include('resources/cacert.pem') }
|
44
|
+
it("should exist") { File.exists?(subject).should be_true }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#http_client" do
|
48
|
+
subject { instance.send(:http_client) }
|
49
|
+
|
50
|
+
context "with a http uri" do
|
51
|
+
it { should be_instance_of(Net::HTTP) }
|
52
|
+
|
53
|
+
its(:use_ssl?) { should be_false }
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with a https uri" do
|
57
|
+
let(:instance) { klass.new('https://www.80beans.com', action, :the_api_key) }
|
58
|
+
|
59
|
+
its(:use_ssl?) { should be_true }
|
60
|
+
its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER }
|
61
|
+
its(:ca_file) { include('resources/cacert.pem') }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal do
|
4
|
+
it { should respond_to :subscriber }
|
5
|
+
|
6
|
+
describe ".transactions" do
|
7
|
+
subject { Appsignal.transactions }
|
8
|
+
|
9
|
+
it { should be_a Hash }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.agent' do
|
13
|
+
subject { Appsignal.agent }
|
14
|
+
|
15
|
+
it { should be_a Appsignal::Agent }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.logger' do
|
19
|
+
subject { Appsignal.logger }
|
20
|
+
|
21
|
+
it { should be_a Logger }
|
22
|
+
its(:level) { should == Logger::INFO }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.config' do
|
26
|
+
subject { Appsignal.config }
|
27
|
+
|
28
|
+
it 'should return the endpoint' do
|
29
|
+
subject[:endpoint].should eq 'http://localhost:3000/1'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return the api key' do
|
33
|
+
subject[:api_key].should eq 'abc'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return ignored exceptions' do
|
37
|
+
subject[:ignore_exceptions].should eq []
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return the slow request threshold' do
|
41
|
+
subject[:slow_request_threshold].should eq 200
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.active?' do
|
46
|
+
subject { Appsignal.active? }
|
47
|
+
|
48
|
+
context "without config" do
|
49
|
+
before { Appsignal.stub(:config => nil) }
|
50
|
+
|
51
|
+
it { should be_false }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with config but inactive" do
|
55
|
+
before { Appsignal.stub(:config => {:active => false}) }
|
56
|
+
|
57
|
+
it { should be_false }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with active config" do
|
61
|
+
before { Appsignal.stub(:config => {:active => true}) }
|
62
|
+
|
63
|
+
it { should be_true }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|