appsignal 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-version +1 -0
- data/README.md +20 -19
- data/appsignal.gemspec +2 -2
- data/lib/appsignal.rb +41 -18
- data/lib/appsignal/agent.rb +28 -54
- data/lib/appsignal/aggregator.rb +65 -0
- data/lib/appsignal/aggregator/post_processor.rb +27 -0
- data/lib/appsignal/config.rb +9 -4
- data/lib/appsignal/listener.rb +30 -0
- data/lib/appsignal/middleware.rb +4 -30
- data/lib/appsignal/middleware/action_view_sanitizer.rb +21 -0
- data/lib/appsignal/middleware/active_record_sanitizer.rb +60 -0
- data/lib/appsignal/middleware/chain.rb +99 -0
- data/lib/appsignal/middleware/delete_blanks.rb +12 -0
- data/lib/appsignal/railtie.rb +9 -1
- data/lib/appsignal/to_appsignal_hash.rb +23 -0
- data/lib/appsignal/transaction.rb +72 -16
- data/lib/appsignal/transaction/params_sanitizer.rb +91 -13
- data/lib/appsignal/transaction/transaction_formatter.rb +32 -68
- data/lib/appsignal/version.rb +1 -1
- data/spec/appsignal/agent_spec.rb +46 -156
- data/spec/appsignal/aggregator/post_processor_spec.rb +84 -0
- data/spec/appsignal/aggregator_spec.rb +182 -0
- data/spec/appsignal/inactive_railtie_spec.rb +2 -1
- data/spec/appsignal/{middleware_spec.rb → listener_spec.rb} +2 -2
- data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +27 -0
- data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +201 -0
- data/spec/appsignal/middleware/chain_spec.rb +168 -0
- data/spec/appsignal/middleware/delete_blanks_spec.rb +37 -0
- data/spec/appsignal/railtie_spec.rb +47 -34
- data/spec/appsignal/to_appsignal_hash_spec.rb +29 -0
- data/spec/appsignal/transaction/params_sanitizer_spec.rb +141 -36
- data/spec/appsignal/transaction/transaction_formatter_spec.rb +60 -155
- data/spec/appsignal/transaction_spec.rb +186 -53
- data/spec/appsignal/transmitter_spec.rb +11 -6
- data/spec/appsignal_spec.rb +33 -0
- data/spec/spec_helper.rb +9 -62
- data/spec/support/helpers/notification_helpers.rb +30 -0
- data/spec/support/helpers/transaction_helpers.rb +64 -0
- metadata +74 -63
- data/.rvmrc +0 -1
- data/lib/appsignal/transaction/faulty_request_formatter.rb +0 -30
- data/lib/appsignal/transaction/regular_request_formatter.rb +0 -11
- data/lib/appsignal/transaction/slow_request_formatter.rb +0 -34
- data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +0 -49
- data/spec/appsignal/transaction/regular_request_formatter_spec.rb +0 -14
- data/spec/appsignal/transaction/slow_request_formatter_spec.rb +0 -76
@@ -1,95 +1,59 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
module Appsignal
|
4
|
-
class TransactionFormatter
|
5
|
-
extend Forwardable
|
4
|
+
class TransactionFormatter < SimpleDelegator
|
6
5
|
|
7
6
|
def initialize(transaction)
|
8
|
-
|
7
|
+
super(transaction)
|
9
8
|
end
|
10
9
|
|
11
|
-
def
|
12
|
-
|
10
|
+
def hash
|
11
|
+
@hash ||= default_hash
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
14
|
+
def to_hash
|
15
|
+
merge_process_action_event_with_log_entry! if process_action_event
|
16
|
+
add_exception_to_hash! if exception?
|
17
|
+
add_events_to_hash! if slow_request?
|
18
|
+
hash
|
17
19
|
end
|
18
20
|
|
19
|
-
|
20
|
-
TransactionFormatter::FaultyRequestFormatter.new(transaction)
|
21
|
-
end
|
21
|
+
protected
|
22
22
|
|
23
|
-
def
|
23
|
+
def default_hash
|
24
24
|
{
|
25
25
|
:request_id => id,
|
26
|
-
:
|
27
|
-
|
26
|
+
:log_entry => {
|
27
|
+
:path => fullpath,
|
28
|
+
:kind => 'http_request',
|
29
|
+
:time => time,
|
30
|
+
:environment => sanitized_environment,
|
31
|
+
:session_data => sanitized_session_data
|
32
|
+
},
|
28
33
|
:failed => exception?
|
29
34
|
}
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def formatted_process_action_event
|
41
|
-
basic_process_action_event.tap { |hsh| hsh.merge!(formatted_payload) if process_action_event }
|
37
|
+
def merge_process_action_event_with_log_entry!
|
38
|
+
hash[:log_entry].merge!(process_action_event.to_appsignal_hash)
|
39
|
+
hash[:log_entry].tap do |o|
|
40
|
+
o.merge!(o.delete(:payload))
|
41
|
+
o[:action] = "#{o.delete(:controller)}##{o.delete(:action)}"
|
42
|
+
o.delete(:name)
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
|
-
def
|
45
|
-
{
|
46
|
-
:
|
47
|
-
:
|
46
|
+
def add_exception_to_hash!
|
47
|
+
hash[:exception] = {
|
48
|
+
:backtrace => exception.backtrace,
|
49
|
+
:exception => exception.name,
|
50
|
+
:message => exception.message
|
48
51
|
}
|
49
52
|
end
|
50
53
|
|
51
|
-
def
|
52
|
-
|
53
|
-
{
|
54
|
-
:duration => process_action_event.duration,
|
55
|
-
:time => process_action_event.time.to_f,
|
56
|
-
:end => process_action_event.end.to_f,
|
57
|
-
:action => action
|
58
|
-
}
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
def sanitized_event_payload(event)
|
63
|
-
Appsignal::ParamsSanitizer.sanitize(
|
64
|
-
Appsignal.event_payload_sanitizer.call(event)
|
65
|
-
)
|
54
|
+
def add_events_to_hash!
|
55
|
+
hash[:events] = events.map(&:to_appsignal_hash)
|
66
56
|
end
|
67
57
|
|
68
|
-
def filtered_environment
|
69
|
-
{}.tap do |out|
|
70
|
-
env.each_pair do |key, value|
|
71
|
-
out[key] = value if ENV_METHODS.include?(key)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Based on what Rails uses + some variables we'd like to show
|
77
|
-
ENV_METHODS = %w{ CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
|
78
|
-
PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER
|
79
|
-
REMOTE_ADDR REQUEST_METHOD SERVER_NAME SERVER_PORT
|
80
|
-
SERVER_PROTOCOL
|
81
|
-
|
82
|
-
HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
|
83
|
-
HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME
|
84
|
-
HTTP_X_APPLICATION_START HTTP_ACCEPT HTTP_ACCEPT_CHARSET
|
85
|
-
HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL
|
86
|
-
HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM HTTP_NEGOTIATE
|
87
|
-
HTTP_PRAGMA HTTP_REFERER }.freeze
|
88
|
-
|
89
58
|
end
|
90
59
|
end
|
91
|
-
|
92
|
-
require 'appsignal/transaction/regular_request_formatter'
|
93
|
-
require 'appsignal/transaction/slow_request_formatter'
|
94
|
-
require 'appsignal/transaction/faulty_request_formatter'
|
95
|
-
require 'appsignal/transaction/params_sanitizer'
|
data/lib/appsignal/version.rb
CHANGED
@@ -1,119 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
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 }
|
3
|
+
class PostProcessingException < Exception
|
4
|
+
end
|
61
5
|
|
62
|
-
|
63
|
-
|
64
|
-
subject.add_to_queue(@slow_transaction)
|
65
|
-
end
|
6
|
+
describe Appsignal::Agent do
|
7
|
+
let(:transaction) { regular_transaction }
|
66
8
|
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
9
|
+
describe "#enqueue" do
|
10
|
+
it "forwards to the aggregator" do
|
11
|
+
subject.aggregator.should respond_to(:add)
|
12
|
+
subject.aggregator.should_receive(:add).with(:foo)
|
110
13
|
end
|
14
|
+
|
15
|
+
after { subject.enqueue(:foo) }
|
111
16
|
end
|
112
17
|
|
113
18
|
describe "#send_queue" do
|
114
19
|
it "transmits" do
|
115
|
-
subject.stub(:
|
116
|
-
subject.transmitter.should_receive(:transmit).with(
|
20
|
+
subject.aggregator.stub(:post_processed_queue! => :foo)
|
21
|
+
subject.transmitter.should_receive(:transmit).with(:foo)
|
117
22
|
end
|
118
23
|
|
119
24
|
it "handles the return code" do
|
@@ -121,42 +26,62 @@ describe Appsignal::Agent do
|
|
121
26
|
subject.should_receive(:handle_result).with('200')
|
122
27
|
end
|
123
28
|
|
29
|
+
it "handle exceptions in post processing" do
|
30
|
+
subject.aggregator.stub(:post_processed_queue!).and_raise(
|
31
|
+
PostProcessingException.new('Message')
|
32
|
+
)
|
33
|
+
|
34
|
+
subject.should_receive(:stop_logging)
|
35
|
+
Appsignal.logger.should_receive(:error).
|
36
|
+
with('PostProcessingException while sending queue: Message').
|
37
|
+
once
|
38
|
+
Appsignal.logger.should_receive(:error).
|
39
|
+
with(kind_of(String)).
|
40
|
+
once
|
41
|
+
end
|
42
|
+
|
124
43
|
it "handles exceptions in transmit" do
|
125
|
-
subject.transmitter.stub(:transmit).and_raise(
|
126
|
-
|
127
|
-
|
44
|
+
subject.transmitter.stub(:transmit).and_raise(
|
45
|
+
Exception.new('Message')
|
46
|
+
)
|
47
|
+
subject.should_receive(:stop_logging)
|
48
|
+
Appsignal.logger.should_receive(:error).
|
49
|
+
with('Exception while sending queue: Message').
|
50
|
+
once
|
51
|
+
Appsignal.logger.should_receive(:error).
|
52
|
+
with(kind_of(String)).
|
53
|
+
once
|
128
54
|
end
|
129
55
|
|
130
56
|
after { subject.send_queue }
|
131
57
|
end
|
132
58
|
|
133
59
|
describe '#handle_result' do
|
134
|
-
before { subject.
|
60
|
+
before { subject.aggregator.add(transaction) }
|
135
61
|
before { subject.instance_variable_set(:@sleep_time, 3.0) }
|
136
62
|
|
137
63
|
context "good responses" do
|
138
|
-
before { subject.handle_result
|
64
|
+
before { subject.send(:handle_result, code) }
|
139
65
|
|
140
66
|
context "with 200" do
|
141
67
|
let(:code) { '200' }
|
142
68
|
|
143
|
-
its(:
|
144
|
-
|
69
|
+
its(:sleep_time) { should == 3.0 }
|
70
|
+
|
71
|
+
it "does not log the event" do
|
72
|
+
Appsignal.logger.should_not_receive(:error)
|
73
|
+
end
|
145
74
|
end
|
146
75
|
|
147
76
|
context "with 420" do
|
148
77
|
let(:code) { '420' }
|
149
78
|
|
150
|
-
its(:queue) { should be_empty }
|
151
|
-
its(:slowest_transactions) { should be_empty }
|
152
79
|
its(:sleep_time) { should == 4.5 }
|
153
80
|
end
|
154
81
|
|
155
82
|
context "with 413" do
|
156
83
|
let(:code) { '413' }
|
157
84
|
|
158
|
-
its(:queue) { should be_empty }
|
159
|
-
its(:slowest_transactions) { should be_empty }
|
160
85
|
its(:sleep_time) { should == 2.0 }
|
161
86
|
end
|
162
87
|
end
|
@@ -197,47 +122,12 @@ describe Appsignal::Agent do
|
|
197
122
|
context "any other response" do
|
198
123
|
let(:code) { 'any other response' }
|
199
124
|
|
200
|
-
it "
|
201
|
-
|
125
|
+
it "logs the event" do
|
126
|
+
Appsignal.logger.should_receive(:error)
|
202
127
|
end
|
203
128
|
end
|
204
129
|
|
205
|
-
after { subject.handle_result
|
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
|
130
|
+
after { subject.send(:handle_result, code) }
|
241
131
|
end
|
242
132
|
end
|
243
133
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::PostProcessor do
|
4
|
+
let(:klass) { Appsignal::PostProcessor }
|
5
|
+
let(:post_processor) { klass.new(transactions) }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
subject { klass.new(:foo) }
|
9
|
+
|
10
|
+
its(:transactions) { should == :foo }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#post_processed_queue!" do
|
14
|
+
let(:first_transaction) { regular_transaction }
|
15
|
+
let(:second_transaction) do
|
16
|
+
slow_transaction(
|
17
|
+
:events => [
|
18
|
+
notification_event(
|
19
|
+
:payload => {
|
20
|
+
:set_value => 'set value',
|
21
|
+
:nil_value => nil
|
22
|
+
}
|
23
|
+
)
|
24
|
+
]
|
25
|
+
)
|
26
|
+
end
|
27
|
+
let(:transactions) { [first_transaction, second_transaction] }
|
28
|
+
let(:processed_queue) { post_processor.post_processed_queue! }
|
29
|
+
subject { processed_queue }
|
30
|
+
|
31
|
+
it { should have(2).items }
|
32
|
+
|
33
|
+
context "the first transaction" do
|
34
|
+
subject { processed_queue[0] }
|
35
|
+
|
36
|
+
it { should_not have_key(:events) }
|
37
|
+
|
38
|
+
context "log entry" do
|
39
|
+
subject { processed_queue[0][:log_entry] }
|
40
|
+
|
41
|
+
it { should have_key(:duration) }
|
42
|
+
it { should have_key(:end) }
|
43
|
+
it { should_not have_key(:events) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "the second transaction" do
|
48
|
+
context "log entry" do
|
49
|
+
subject { processed_queue[1][:log_entry] }
|
50
|
+
|
51
|
+
it { should have_key(:duration) }
|
52
|
+
it { should have_key(:end) }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "first event" do
|
56
|
+
subject { processed_queue[1][:events][0] }
|
57
|
+
|
58
|
+
it { should_not be_nil }
|
59
|
+
it { should have_key(:name) }
|
60
|
+
|
61
|
+
it "should have the set value in the payload" do
|
62
|
+
subject[:payload][:set_value].should == 'set value'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not have the nil value in the payload"\
|
66
|
+
"since delete blanks is in the middle ware stack" do
|
67
|
+
subject[:payload].should_not have_key(:nil_value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe ".default_middleware" do
|
74
|
+
subject { klass.default_middleware }
|
75
|
+
|
76
|
+
it { should be_instance_of Appsignal::Middleware::Chain }
|
77
|
+
|
78
|
+
it "should include the default middleware stack" do
|
79
|
+
subject.exists?(Appsignal::Middleware::DeleteBlanks).should be_true
|
80
|
+
subject.exists?(Appsignal::Middleware::ActionViewSanitizer).should be_true
|
81
|
+
subject.exists?(Appsignal::Middleware::ActiveRecordSanitizer).should be_true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|