appsignal 0.4.7 → 0.5.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 (47) hide show
  1. data/.ruby-version +1 -0
  2. data/README.md +20 -19
  3. data/appsignal.gemspec +2 -2
  4. data/lib/appsignal.rb +41 -18
  5. data/lib/appsignal/agent.rb +28 -54
  6. data/lib/appsignal/aggregator.rb +65 -0
  7. data/lib/appsignal/aggregator/post_processor.rb +27 -0
  8. data/lib/appsignal/config.rb +9 -4
  9. data/lib/appsignal/listener.rb +30 -0
  10. data/lib/appsignal/middleware.rb +4 -30
  11. data/lib/appsignal/middleware/action_view_sanitizer.rb +21 -0
  12. data/lib/appsignal/middleware/active_record_sanitizer.rb +60 -0
  13. data/lib/appsignal/middleware/chain.rb +99 -0
  14. data/lib/appsignal/middleware/delete_blanks.rb +12 -0
  15. data/lib/appsignal/railtie.rb +9 -1
  16. data/lib/appsignal/to_appsignal_hash.rb +23 -0
  17. data/lib/appsignal/transaction.rb +72 -16
  18. data/lib/appsignal/transaction/params_sanitizer.rb +91 -13
  19. data/lib/appsignal/transaction/transaction_formatter.rb +32 -68
  20. data/lib/appsignal/version.rb +1 -1
  21. data/spec/appsignal/agent_spec.rb +46 -156
  22. data/spec/appsignal/aggregator/post_processor_spec.rb +84 -0
  23. data/spec/appsignal/aggregator_spec.rb +182 -0
  24. data/spec/appsignal/inactive_railtie_spec.rb +2 -1
  25. data/spec/appsignal/{middleware_spec.rb → listener_spec.rb} +2 -2
  26. data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +27 -0
  27. data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +201 -0
  28. data/spec/appsignal/middleware/chain_spec.rb +168 -0
  29. data/spec/appsignal/middleware/delete_blanks_spec.rb +37 -0
  30. data/spec/appsignal/railtie_spec.rb +47 -34
  31. data/spec/appsignal/to_appsignal_hash_spec.rb +29 -0
  32. data/spec/appsignal/transaction/params_sanitizer_spec.rb +141 -36
  33. data/spec/appsignal/transaction/transaction_formatter_spec.rb +60 -155
  34. data/spec/appsignal/transaction_spec.rb +186 -53
  35. data/spec/appsignal/transmitter_spec.rb +11 -6
  36. data/spec/appsignal_spec.rb +33 -0
  37. data/spec/spec_helper.rb +9 -62
  38. data/spec/support/helpers/notification_helpers.rb +30 -0
  39. data/spec/support/helpers/transaction_helpers.rb +64 -0
  40. metadata +74 -63
  41. data/.rvmrc +0 -1
  42. data/lib/appsignal/transaction/faulty_request_formatter.rb +0 -30
  43. data/lib/appsignal/transaction/regular_request_formatter.rb +0 -11
  44. data/lib/appsignal/transaction/slow_request_formatter.rb +0 -34
  45. data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +0 -49
  46. data/spec/appsignal/transaction/regular_request_formatter_spec.rb +0 -14
  47. 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
- @transaction = transaction
7
+ super(transaction)
9
8
  end
10
9
 
11
- def self.regular(transaction)
12
- TransactionFormatter::RegularRequestFormatter.new(transaction)
10
+ def hash
11
+ @hash ||= default_hash
13
12
  end
14
13
 
15
- def self.slow(transaction)
16
- TransactionFormatter::SlowRequestFormatter.new(transaction)
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
- def self.faulty(transaction)
20
- TransactionFormatter::FaultyRequestFormatter.new(transaction)
21
- end
21
+ protected
22
22
 
23
- def to_hash
23
+ def default_hash
24
24
  {
25
25
  :request_id => id,
26
- :action => action,
27
- :log_entry => formatted_process_action_event,
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
- protected
33
-
34
- def_delegators :transaction, :id, :events, :exception, :exception?, :env,
35
- :request, :process_action_event, :action
36
- def_delegators :process_action_event, :payload
37
-
38
- attr_reader :transaction
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 basic_process_action_event
45
- {
46
- :path => request.fullpath,
47
- :kind => 'http_request'
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 formatted_payload
52
- sanitized_event_payload(process_action_event).merge(
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'
@@ -1,3 +1,3 @@
1
1
  module Appsignal
2
- VERSION = '0.4.7'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -1,119 +1,24 @@
1
1
  require 'spec_helper'
2
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 }
3
+ class PostProcessingException < Exception
4
+ end
61
5
 
62
- context "a slow transaction" do
63
- before do
64
- subject.add_to_queue(@slow_transaction)
65
- end
6
+ describe Appsignal::Agent do
7
+ let(:transaction) { regular_transaction }
66
8
 
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
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(:queue => [stub(:to_hash => 'foo')])
116
- subject.transmitter.should_receive(:transmit).with(['foo'])
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(Exception.new)
126
- subject.should_receive(:handle_result).with(nil)
127
- Appsignal.logger.should_receive(:error).with('Exception while communicating with AppSignal: Exception')
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.add_to_queue(transaction) }
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(code) }
64
+ before { subject.send(:handle_result, code) }
139
65
 
140
66
  context "with 200" do
141
67
  let(:code) { '200' }
142
68
 
143
- its(:queue) { should be_empty }
144
- its(:slowest_transactions) { should be_empty }
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 "calls retry_once" do
201
- subject.should_receive :retry_once
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(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
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