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.
- 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
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p392
|
data/README.md
CHANGED
@@ -13,36 +13,37 @@ example
|
|
13
13
|
|
14
14
|
tagging [2]
|
15
15
|
|
16
|
-
##
|
16
|
+
## Postprocessing middleware
|
17
17
|
Appsignal logs Rails
|
18
18
|
[ActiveSupport::Notification](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html)-events
|
19
19
|
to appsignal.com over SSL. These events contain basic metadata such as a name
|
20
|
-
and timestamps, and additional 'payload' log data.
|
21
|
-
|
22
|
-
|
23
|
-
event payload sanitizer in `config/environment/my_env.rb`. The
|
24
|
-
`event_payload_sanitizer` needs to be a callable object that returns a
|
25
|
-
JSON-serializable hash.
|
20
|
+
and timestamps, and additional 'payload' log data. Appsignal uses a postprocessing
|
21
|
+
middleware stack to clean up events before they get sent to appsignal.com. You
|
22
|
+
can add your own middleware to this stack in `config/environment/my_env.rb`.
|
26
23
|
|
27
24
|
### Examples
|
28
25
|
|
29
|
-
####
|
26
|
+
#### Minimal template
|
30
27
|
```ruby
|
31
|
-
|
32
|
-
|
28
|
+
class MiddlewareTemplate
|
29
|
+
def call(event)
|
30
|
+
# modify the event in place
|
31
|
+
yield # pass control to the next middleware
|
32
|
+
# modify the event some more
|
33
|
+
end
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
```ruby
|
36
|
-
Appsignal.event_payload_sanitizer = proc { {} }
|
36
|
+
Appsignal.postprocessing_middleware.add MiddlewareTemplate
|
37
37
|
```
|
38
38
|
|
39
|
-
####
|
39
|
+
#### Remove boring payloads
|
40
40
|
```ruby
|
41
|
-
|
42
|
-
|
43
|
-
event.
|
44
|
-
|
45
|
-
|
41
|
+
class RemoveBoringPayload
|
42
|
+
def call(event)
|
43
|
+
unless event.name == 'interesting'
|
44
|
+
event.payload = {}
|
45
|
+
end
|
46
|
+
yield
|
46
47
|
end
|
47
48
|
end
|
48
49
|
```
|
data/appsignal.gemspec
CHANGED
@@ -22,12 +22,12 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.require_paths = ['lib']
|
23
23
|
gem.version = Appsignal::VERSION
|
24
24
|
|
25
|
-
gem.add_dependency 'rails', '~>3'
|
25
|
+
gem.add_dependency 'rails', '~> 3.0'
|
26
26
|
gem.add_dependency 'rake'
|
27
27
|
gem.add_dependency 'json'
|
28
|
-
gem.add_dependency 'rack'
|
29
28
|
|
30
29
|
gem.add_development_dependency 'rspec'
|
31
30
|
gem.add_development_dependency 'capistrano'
|
32
31
|
gem.add_development_dependency 'generator_spec'
|
32
|
+
gem.add_development_dependency 'pry'
|
33
33
|
end
|
data/lib/appsignal.rb
CHANGED
@@ -2,18 +2,17 @@ raise 'This appsignal gem only works with rails' unless defined?(Rails)
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
class << self
|
5
|
-
attr_accessor :subscriber
|
5
|
+
attr_accessor :subscriber
|
6
|
+
attr_reader :in_memory_log
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
8
|
+
# Convenience method for adding a transaction to the queue. This queue is
|
9
|
+
# managed and is periodically pushed to Appsignal.
|
10
|
+
#
|
11
|
+
# @return [ true ] True.
|
12
|
+
#
|
13
|
+
# @since 0.5.0
|
14
|
+
def enqueue(transaction)
|
15
|
+
agent.enqueue(transaction)
|
17
16
|
end
|
18
17
|
|
19
18
|
def transactions
|
@@ -24,24 +23,48 @@ module Appsignal
|
|
24
23
|
@agent ||= Appsignal::Agent.new
|
25
24
|
end
|
26
25
|
|
26
|
+
def logger
|
27
|
+
@in_memory_log = StringIO.new unless @in_memory_log
|
28
|
+
@logger ||= Logger.new(@in_memory_log).tap do |l|
|
29
|
+
l.level = Logger::INFO
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def flush_in_memory_log
|
34
|
+
Appsignal.logger << @in_memory_log.string
|
35
|
+
end
|
36
|
+
|
37
|
+
def logger=(l)
|
38
|
+
@logger = l
|
39
|
+
end
|
40
|
+
|
27
41
|
def config
|
28
42
|
@config ||= Appsignal::Config.new(Rails.root, Rails.env).load
|
29
43
|
end
|
30
44
|
|
31
|
-
def
|
32
|
-
@
|
45
|
+
def post_processing_middleware
|
46
|
+
@post_processing_chain ||= PostProcessor.default_middleware
|
47
|
+
yield @post_processing_chain if block_given?
|
48
|
+
@post_processing_chain
|
33
49
|
end
|
50
|
+
|
51
|
+
def active?
|
52
|
+
config && config[:active] == true
|
53
|
+
end
|
54
|
+
|
34
55
|
end
|
35
56
|
end
|
36
57
|
|
58
|
+
require 'appsignal/agent'
|
59
|
+
require 'appsignal/aggregator'
|
60
|
+
require 'appsignal/auth_check'
|
37
61
|
require 'appsignal/cli'
|
38
62
|
require 'appsignal/config'
|
39
|
-
require 'appsignal/
|
40
|
-
require 'appsignal/
|
63
|
+
require 'appsignal/exception_notification'
|
64
|
+
require 'appsignal/listener'
|
41
65
|
require 'appsignal/marker'
|
42
66
|
require 'appsignal/middleware'
|
67
|
+
require 'appsignal/railtie'
|
43
68
|
require 'appsignal/transaction'
|
44
|
-
require 'appsignal/
|
45
|
-
require 'appsignal/auth_check'
|
69
|
+
require 'appsignal/transmitter'
|
46
70
|
require 'appsignal/version'
|
47
|
-
require 'appsignal/railtie'
|
data/lib/appsignal/agent.rb
CHANGED
@@ -1,104 +1,78 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class Agent
|
3
|
-
attr_reader :queue, :active, :sleep_time, :slowest_transactions, :transmitter
|
4
3
|
ACTION = 'log_entries'
|
5
4
|
|
5
|
+
attr_reader :aggregator, :active, :sleep_time, :transmitter
|
6
|
+
|
6
7
|
def initialize
|
7
8
|
return unless Appsignal.active?
|
8
9
|
@sleep_time = 60.0
|
9
|
-
@
|
10
|
-
@queue = []
|
10
|
+
@aggregator = Aggregator.new
|
11
11
|
@retry_request = true
|
12
12
|
@thread = Thread.new do
|
13
13
|
while true do
|
14
|
-
send_queue if
|
15
|
-
sleep
|
14
|
+
send_queue if aggregator.has_transactions?
|
15
|
+
sleep(sleep_time)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
@transmitter = Transmitter.new(
|
19
|
-
Appsignal.config
|
19
|
+
Appsignal.config.fetch(:endpoint),
|
20
20
|
ACTION,
|
21
|
-
Appsignal.config
|
21
|
+
Appsignal.config.fetch(:api_key)
|
22
22
|
)
|
23
23
|
Appsignal.logger.info 'Started the Appsignal agent'
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
current_slowest_transaction = @slowest_transactions[transaction.action]
|
29
|
-
if current_slowest_transaction
|
30
|
-
if current_slowest_transaction.process_action_event.duration <
|
31
|
-
transaction.process_action_event.duration
|
32
|
-
current_slowest_transaction.clear_payload_and_events!
|
33
|
-
@slowest_transactions[transaction.action] = transaction
|
34
|
-
else
|
35
|
-
transaction.clear_payload_and_events!
|
36
|
-
end
|
37
|
-
else
|
38
|
-
@slowest_transactions[transaction.action] = transaction
|
39
|
-
end
|
40
|
-
end
|
41
|
-
@queue << transaction
|
26
|
+
def enqueue(transaction)
|
27
|
+
aggregator.add(transaction)
|
42
28
|
end
|
43
29
|
|
44
30
|
def send_queue
|
45
31
|
Appsignal.logger.debug "Sending queue"
|
32
|
+
current_aggregator = aggregator
|
33
|
+
@aggregator = Aggregator.new
|
46
34
|
begin
|
47
|
-
handle_result transmitter.transmit(
|
35
|
+
handle_result transmitter.transmit(current_aggregator.post_processed_queue!)
|
48
36
|
rescue Exception => ex
|
49
|
-
Appsignal.logger.error "
|
50
|
-
|
37
|
+
Appsignal.logger.error "#{ex.class} while sending queue: #{ex.message}"
|
38
|
+
Appsignal.logger.error ex.backtrace.join('\n')
|
39
|
+
stop_logging
|
51
40
|
end
|
52
41
|
end
|
53
42
|
|
43
|
+
protected
|
44
|
+
|
54
45
|
def handle_result(code)
|
55
46
|
Appsignal.logger.debug "Queue sent, response code: #{code}"
|
56
47
|
case code.to_i
|
57
|
-
when 200
|
58
|
-
good_response
|
48
|
+
when 200 # ok
|
59
49
|
when 420 # Enhance Your Calm
|
60
|
-
|
61
|
-
@sleep_time = @sleep_time * 1.5
|
50
|
+
@sleep_time = sleep_time * 1.5
|
62
51
|
when 413 # Request Entity Too Large
|
63
|
-
|
64
|
-
@sleep_time = @sleep_time / 1.5
|
52
|
+
@sleep_time = sleep_time / 1.5
|
65
53
|
when 429
|
66
|
-
Appsignal.logger.error "Too many requests sent
|
54
|
+
Appsignal.logger.error "Too many requests sent"
|
67
55
|
stop_logging
|
68
56
|
when 406
|
69
|
-
Appsignal.logger.error "Your appsignal gem cannot communicate with
|
57
|
+
Appsignal.logger.error "Your appsignal gem cannot communicate with "\
|
58
|
+
"the API anymore, please upgrade."
|
70
59
|
stop_logging
|
71
60
|
when 402
|
72
|
-
Appsignal.logger.error "Payment required
|
61
|
+
Appsignal.logger.error "Payment required"
|
73
62
|
stop_logging
|
74
63
|
when 401
|
75
|
-
Appsignal.logger.error "API token cannot be authorized
|
64
|
+
Appsignal.logger.error "API token cannot be authorized"
|
76
65
|
stop_logging
|
77
66
|
else
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
protected
|
83
|
-
|
84
|
-
def good_response
|
85
|
-
@queue = []
|
86
|
-
@slowest_transactions = {}
|
87
|
-
@retry_request = true
|
88
|
-
end
|
89
|
-
|
90
|
-
def retry_once
|
91
|
-
if @retry_request
|
92
|
-
@retry_request = false
|
93
|
-
else
|
94
|
-
@retry_request = true
|
95
|
-
@queue = []
|
67
|
+
Appsignal.logger.error "Unknown Appsignal response code: '#{code}'"
|
96
68
|
end
|
97
69
|
end
|
98
70
|
|
99
71
|
def stop_logging
|
72
|
+
Appsignal.logger.info("Disengaging the agent")
|
100
73
|
ActiveSupport::Notifications.unsubscribe(Appsignal.subscriber)
|
101
74
|
Thread.kill(@thread)
|
102
75
|
end
|
76
|
+
|
103
77
|
end
|
104
78
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Appsignal
|
2
|
+
class Aggregator
|
3
|
+
attr_reader :queue, :slowness_index
|
4
|
+
|
5
|
+
def initialize(queue = [], slowness_index = {})
|
6
|
+
@queue = queue
|
7
|
+
@slowness_index = slowness_index
|
8
|
+
end
|
9
|
+
|
10
|
+
# truncates or reduces the size of event values of the transaction, and
|
11
|
+
# adds it to the queue.
|
12
|
+
#
|
13
|
+
# @returns [ Array ] Array with transactions
|
14
|
+
def add(transaction)
|
15
|
+
case transaction.type
|
16
|
+
when :regular_request
|
17
|
+
transaction.truncate!
|
18
|
+
when :slow_request
|
19
|
+
pre_process_slowness!(transaction)
|
20
|
+
when :exception
|
21
|
+
transaction.convert_values_to_primitives!
|
22
|
+
end
|
23
|
+
queue << transaction
|
24
|
+
end
|
25
|
+
|
26
|
+
# Informs whether the queue has any transactions in it or not
|
27
|
+
#
|
28
|
+
# @returns [ Boolean ]
|
29
|
+
def has_transactions?
|
30
|
+
queue.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Post process the queue and return it
|
34
|
+
#
|
35
|
+
# @returns [ Array ] Array of post processed Appsignal::Transaction objects
|
36
|
+
def post_processed_queue!
|
37
|
+
Appsignal::PostProcessor.new(queue).post_processed_queue!
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def similar_slowest(transaction)
|
43
|
+
slowness_index[transaction.action]
|
44
|
+
end
|
45
|
+
|
46
|
+
def pre_process_slowness!(transaction)
|
47
|
+
similar_slowest = similar_slowest(transaction)
|
48
|
+
if similar_slowest
|
49
|
+
if transaction.slower?(similar_slowest)
|
50
|
+
slowness_index[transaction.action] = transaction
|
51
|
+
transaction.convert_values_to_primitives!
|
52
|
+
similar_slowest.truncate!
|
53
|
+
else
|
54
|
+
transaction.truncate!
|
55
|
+
end
|
56
|
+
else
|
57
|
+
slowness_index[transaction.action] = transaction
|
58
|
+
transaction.convert_values_to_primitives!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
require 'appsignal/aggregator/post_processor'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Appsignal
|
2
|
+
class PostProcessor
|
3
|
+
attr_reader :transactions
|
4
|
+
|
5
|
+
def initialize(transactions)
|
6
|
+
@transactions = transactions
|
7
|
+
end
|
8
|
+
|
9
|
+
def post_processed_queue!
|
10
|
+
transactions.map do |transaction|
|
11
|
+
transaction.events.each do |event|
|
12
|
+
Appsignal.post_processing_middleware.invoke(event)
|
13
|
+
end
|
14
|
+
transaction.to_hash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.default_middleware
|
19
|
+
Middleware::Chain.new do |chain|
|
20
|
+
chain.add Appsignal::Middleware::DeleteBlanks
|
21
|
+
chain.add Appsignal::Middleware::ActionViewSanitizer
|
22
|
+
chain.add Appsignal::Middleware::ActiveRecordSanitizer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/appsignal/config.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
require 'appsignal/careful_logger'
|
2
2
|
|
3
3
|
module Appsignal
|
4
|
+
|
4
5
|
class Config
|
5
6
|
include Appsignal::CarefulLogger
|
6
7
|
|
8
|
+
DEFAULT_CONFIG = {
|
9
|
+
:ignore_exceptions => [],
|
10
|
+
:endpoint => 'https://push.appsignal.com/1',
|
11
|
+
:slow_request_threshold => 200
|
12
|
+
}.freeze
|
13
|
+
|
7
14
|
attr_accessor :root_path, :rails_env
|
8
15
|
|
9
16
|
def initialize(root_path, rails_env, logger=Appsignal.logger)
|
@@ -25,11 +32,9 @@ module Appsignal
|
|
25
32
|
return
|
26
33
|
end
|
27
34
|
|
28
|
-
config
|
29
|
-
:endpoint => 'https://push.appsignal.com/1',
|
30
|
-
:slow_request_threshold => 200
|
31
|
-
}.merge(config.symbolize_keys)
|
35
|
+
DEFAULT_CONFIG.merge(config.symbolize_keys)
|
32
36
|
end
|
33
37
|
|
34
38
|
end
|
39
|
+
|
35
40
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'action_dispatch'
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
class Listener
|
5
|
+
def initialize(app, options = {})
|
6
|
+
@app, @options = app, options
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
Appsignal::Transaction.create(env['action_dispatch.request_id'], env)
|
11
|
+
@app.call(env)
|
12
|
+
rescue Exception => exception
|
13
|
+
unless in_ignored_exceptions?(exception)
|
14
|
+
Appsignal::Transaction.current.add_exception(
|
15
|
+
Appsignal::ExceptionNotification.new(env, exception)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
raise exception
|
19
|
+
ensure
|
20
|
+
Appsignal::Transaction.current.complete!
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def in_ignored_exceptions?(exception)
|
26
|
+
Array.wrap(Appsignal.config[:ignore_exceptions]).
|
27
|
+
include?(exception.class.name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|