amqp_logging 0.4.0 → 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/Readme.rdoc +24 -24
- data/lib/amqp_logging.rb +7 -89
- data/lib/amqp_logging/log_device.rb +65 -0
- data/lib/amqp_logging/logger.rb +23 -0
- data/lib/amqp_logging/metrics_agent.rb +91 -0
- data/lib/amqp_logging/metrics_agent/rails.rb +16 -0
- data/lib/amqp_logging/metrics_agent/rails/after_dispatch_callback_handler.rb +28 -0
- data/test/{amqp_logger_test.rb → logger_test.rb} +19 -30
- data/test/metrics_agent_test.rb +108 -0
- data/test/test_helper.rb +3 -0
- metadata +44 -7
data/Readme.rdoc
CHANGED
@@ -1,24 +1,19 @@
|
|
1
1
|
=AMQPLogging
|
2
2
|
|
3
|
-
|
4
|
-
A ruby logger class that logs to an {AMQP}[http://www.amqp.org/] exchange in addition to your default log device.
|
3
|
+
A ruby logger class that logs to an {AMQP}[http://www.amqp.org/] exchange
|
5
4
|
|
6
5
|
==Basic Configuration
|
7
6
|
By default the logs are routed to the host, exchange and key specified in DEFAULT_OPTIONS.
|
8
7
|
You can change the configuration when creating the logger object by specifying an argument hash:
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
logging_config = { :routing_key => "applogging",
|
13
|
-
:host => AppConfig.amqp_logging.host,
|
14
|
-
:exchange => AppConfig.amqp_logging.exchange }
|
9
|
+
require 'amqp_logging'
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
With Rails 3 you will need to modify this a bit, as config.log_path no longer has the desired effect:
|
11
|
+
logging_config = { :routing_key => "applogging",
|
12
|
+
:host => AppConfig.amqp_logging.host,
|
13
|
+
:exchange => AppConfig.amqp_logging.exchange }
|
20
14
|
|
21
|
-
|
15
|
+
logger = AMQPLogging::Logger.new(logging_config)
|
16
|
+
config.logger = logger
|
22
17
|
|
23
18
|
==Routing Keys
|
24
19
|
|
@@ -27,18 +22,23 @@ receives the logline as the first argument and returns the routing key.
|
|
27
22
|
|
28
23
|
Example:
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
25
|
+
# You can use a lambda or whatever responds to #call as the routing key generator
|
26
|
+
AMQPRoutingKeyGenerator = lambda do |logline|
|
27
|
+
if logline =~ /(?:engine\[([^\]]*)\])\: (Completed in|Processing|Session ID)?/
|
28
|
+
key = "logs.app.#{$1}"
|
29
|
+
key << ".statistics" unless $2.nil?
|
30
|
+
else
|
31
|
+
key = "logs.app.system"
|
32
|
+
end
|
33
|
+
key
|
34
|
+
end
|
35
|
+
|
36
|
+
AMQPLogging::Logger.new($stdout, :routing_key => AMQPRoutingKeyGenerator)
|
37
|
+
|
38
|
+
==Fallback to disk
|
39
|
+
|
40
|
+
The support for a fallback logdevice was removed with version 0.5.0. A multi logger thatallows
|
41
|
+
you get the same behaviour will follow soonish.
|
42
42
|
|
43
43
|
==License
|
44
44
|
|
data/lib/amqp_logging.rb
CHANGED
@@ -1,99 +1,17 @@
|
|
1
1
|
require 'bunny'
|
2
2
|
|
3
3
|
begin
|
4
|
-
|
4
|
+
# ActiveSupport 3.x
|
5
|
+
require 'active_support/time'
|
6
|
+
require 'active_support/core_ext/hash/slice'
|
5
7
|
rescue LoadError
|
6
8
|
require 'active_support' # ActiveSupport 2.x
|
7
9
|
end
|
8
10
|
|
9
11
|
module AMQPLogging
|
10
|
-
|
11
|
-
DEFAULT_OPTIONS = {
|
12
|
-
:shift_age => 0,
|
13
|
-
:shift_size => 1048576,
|
14
|
-
:host => "localhost",
|
15
|
-
:exchange => "logging_exchange",
|
16
|
-
:queue => "logging_queue",
|
17
|
-
:routing_key => "logs"
|
18
|
-
}
|
19
|
-
|
20
|
-
RETRY_AFTER = 10.seconds
|
21
|
-
|
22
|
-
class Logger < ::Logger
|
23
|
-
attr_accessor :extra_attributes
|
24
|
-
attr_accessor :errback
|
25
|
-
|
26
|
-
def initialize(logdev, *args)
|
27
|
-
options = args.first.is_a?(Hash) ? DEFAULT_OPTIONS.merge(args.first) : DEFAULT_OPTIONS
|
28
|
-
super(logdev, options[:shift_age], options[:shift_size])
|
29
|
-
@logdev = AMQPLogDevice.new(@logdev, options)
|
30
|
-
@logdev.logger = self
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class AMQPLogDevice
|
35
|
-
attr_reader :exchange, :configuration
|
36
|
-
attr_accessor :logger
|
37
|
-
|
38
|
-
def initialize(dev, opts = {})
|
39
|
-
@configuration = opts
|
40
|
-
@fallback_logdev = dev
|
41
|
-
end
|
42
|
-
|
43
|
-
def write(msg)
|
44
|
-
begin
|
45
|
-
if !@paused || @paused <= RETRY_AFTER.ago
|
46
|
-
routing_key = configuration[:routing_key].respond_to?(:call) ? configuration[:routing_key].call(msg).to_s : configuration[:routing_key]
|
47
|
-
exchange.publish(msg, :key => routing_key)
|
48
|
-
end
|
49
|
-
rescue Exception => exception
|
50
|
-
reraise_expectation_errors!
|
51
|
-
pause_amqp_logging(exception)
|
52
|
-
ensure
|
53
|
-
@fallback_logdev.write(msg)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def close
|
58
|
-
@fallback_logdev.close
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
def pause_amqp_logging(exception)
|
63
|
-
@paused = Time.now
|
64
|
-
reset_amqp
|
65
|
-
logger.errback.call(exception) if logger.errback && logger.errback.respond_to?(:call)
|
66
|
-
end
|
67
|
-
|
68
|
-
def reset_amqp
|
69
|
-
begin
|
70
|
-
bunny.stop if bunny.connected?
|
71
|
-
rescue
|
72
|
-
# if bunny throws an exception here, its not usable anymore anyway
|
73
|
-
ensure
|
74
|
-
@exchange = @bunny = nil
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def exchange
|
79
|
-
bunny.start unless bunny.connected?
|
80
|
-
@exchange ||= bunny.exchange(configuration[:exchange], :type => :topic)
|
81
|
-
end
|
82
|
-
|
83
|
-
def bunny
|
84
|
-
@bunny ||= Bunny.new(:host => configuration[:host])
|
85
|
-
@bunny
|
86
|
-
end
|
87
|
-
|
88
|
-
if defined?(Mocha)
|
89
|
-
def reraise_expectation_errors! #:nodoc:
|
90
|
-
raise if $!.is_a?(Mocha::ExpectationError)
|
91
|
-
end
|
92
|
-
else
|
93
|
-
def reraise_expectation_errors! #:nodoc:
|
94
|
-
# noop
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
12
|
+
autoload :MetricsAgent, 'amqp_logging/metrics_agent'
|
98
13
|
end
|
99
14
|
|
15
|
+
require 'logger'
|
16
|
+
require 'amqp_logging/logger'
|
17
|
+
require 'amqp_logging/log_device'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class AMQPLogging::LogDevice
|
2
|
+
RETRY_AFTER = 10.seconds
|
3
|
+
|
4
|
+
attr_reader :exchange, :configuration
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@configuration = opts
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(msg)
|
12
|
+
begin
|
13
|
+
if !@paused || @paused <= RETRY_AFTER.ago
|
14
|
+
routing_key = configuration[:routing_key].respond_to?(:call) ? configuration[:routing_key].call(msg).to_s : configuration[:routing_key]
|
15
|
+
exchange.publish(msg, :key => routing_key)
|
16
|
+
end
|
17
|
+
rescue Exception => exception
|
18
|
+
reraise_expectation_errors!
|
19
|
+
pause_amqp_logging(exception)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def close
|
24
|
+
reset_amqp # TODO: Test!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def pause_amqp_logging(exception)
|
29
|
+
@paused = Time.now
|
30
|
+
reset_amqp
|
31
|
+
logger.errback.call(exception) if logger.errback && logger.errback.respond_to?(:call)
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset_amqp
|
35
|
+
begin
|
36
|
+
bunny.stop if bunny.connected?
|
37
|
+
rescue
|
38
|
+
# if bunny throws an exception here, its not usable anymore anyway
|
39
|
+
ensure
|
40
|
+
@exchange = @bunny = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def exchange
|
45
|
+
bunny.start unless bunny.connected?
|
46
|
+
@exchange ||= bunny.exchange(configuration[:exchange], :durable => configuration[:exchange_durable],
|
47
|
+
:auto_delete => configuration[:exchange_auto_delete],
|
48
|
+
:type => configuration[:exchange_type])
|
49
|
+
end
|
50
|
+
|
51
|
+
def bunny
|
52
|
+
@bunny ||= Bunny.new(configuration.slice(:host, :port, :user, :pass))
|
53
|
+
@bunny
|
54
|
+
end
|
55
|
+
|
56
|
+
if defined?(Mocha)
|
57
|
+
def reraise_expectation_errors! #:nodoc:
|
58
|
+
raise if $!.is_a?(Mocha::ExpectationError)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
def reraise_expectation_errors! #:nodoc:
|
62
|
+
# noop
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class AMQPLogging::Logger < ::Logger
|
2
|
+
DEFAULT_OPTIONS = {
|
3
|
+
:shift_age => 0,
|
4
|
+
:shift_size => 1048576,
|
5
|
+
:host => "localhost",
|
6
|
+
:exchange => "logging_exchange",
|
7
|
+
:queue => "logging_queue",
|
8
|
+
:routing_key => "logs",
|
9
|
+
:exchange_durable => true,
|
10
|
+
:exchange_auto_delete => false,
|
11
|
+
:exchange_type => :topic,
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_accessor :extra_attributes
|
15
|
+
attr_accessor :errback
|
16
|
+
|
17
|
+
def initialize(options=DEFAULT_OPTIONS)
|
18
|
+
options = DEFAULT_OPTIONS.merge(options)
|
19
|
+
super(nil)
|
20
|
+
@logdev = AMQPLogging::LogDevice.new(options)
|
21
|
+
@logdev.logger = self
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
|
4
|
+
module AMQPLogging
|
5
|
+
class MetricsAgent
|
6
|
+
attr_reader :fields
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@default_fields = {
|
10
|
+
:host => Socket.gethostname.split('.').first,
|
11
|
+
:pid => Process.pid,
|
12
|
+
:loglines => {
|
13
|
+
:default => []
|
14
|
+
}
|
15
|
+
}
|
16
|
+
@logger_types = {}
|
17
|
+
reset_fields
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
@logger || (self.logger = ::Logger.new($stdout))
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger=(logger)
|
25
|
+
@logger = logger
|
26
|
+
@logger.formatter = Proc.new {|_, _, msg, progname| msg || progname}
|
27
|
+
@logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def flush
|
31
|
+
logger.info(@fields.to_json + "\n")
|
32
|
+
reset_fields
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](fieldname)
|
36
|
+
@fields[fieldname]
|
37
|
+
end
|
38
|
+
|
39
|
+
def []=(fieldname, value)
|
40
|
+
@fields[fieldname] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_logline(severity, message, progname, logger)
|
44
|
+
t = Time.now
|
45
|
+
msg = (message || progname).strip
|
46
|
+
@fields[:loglines][@logger_types[logger]] << [severity, t.strftime("%Y-%d-%mT%H:%M:%S.#{t.usec}"), msg]
|
47
|
+
end
|
48
|
+
|
49
|
+
def wrap_logger(logger, type = :default)
|
50
|
+
agent = self
|
51
|
+
register_logger(logger, type)
|
52
|
+
logger.instance_eval do
|
53
|
+
@agent = agent
|
54
|
+
class << self
|
55
|
+
include MetricsAgentSupport
|
56
|
+
end
|
57
|
+
end
|
58
|
+
logger
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def register_logger(logger, type)
|
63
|
+
@logger_types[logger] = type
|
64
|
+
@fields[:loglines][type] = []
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset_fields
|
68
|
+
@fields = {
|
69
|
+
}.merge!(@default_fields)
|
70
|
+
@logger_types.values.each {|logtype| @fields[:loglines][logtype] = []}
|
71
|
+
end
|
72
|
+
|
73
|
+
module MetricsAgentSupport
|
74
|
+
def self.included(base)
|
75
|
+
base.class_eval do
|
76
|
+
alias :add_without_proxy :add
|
77
|
+
alias :add :add_with_proxy
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def agent
|
82
|
+
@agent
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_with_proxy(severity, message = nil, progname = nil)
|
86
|
+
@agent.add_logline(severity, message, progname, self) unless severity < @level
|
87
|
+
add_without_proxy(severity, message, progname)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'amqp_logging'
|
2
|
+
require 'amqp_logging/metrics_agent/rails/after_dispatch_callback_handler'
|
3
|
+
|
4
|
+
ActionController::Dispatcher.after_dispatch do |dispatcher|
|
5
|
+
AMQPLogging::Rails::AfterDispatchCallbackHandler.run(dispatcher)
|
6
|
+
end
|
7
|
+
|
8
|
+
class ActionController::Base
|
9
|
+
def log_processing_for_request_id_with_metrics_agent
|
10
|
+
logger.agent[:page] = "#{self.class.name}\##{action_name}"
|
11
|
+
t = Time.now
|
12
|
+
logger.agent[:started_at] = t.strftime("%Y-%d-%mT%H:%M:%S.#{t.usec}")
|
13
|
+
log_processing_for_request_id_without_metrics_agent
|
14
|
+
end
|
15
|
+
alias_method_chain :log_processing_for_request_id, :metrics_agent
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AMQPLogging
|
2
|
+
module Rails
|
3
|
+
class AfterDispatchCallbackHandler
|
4
|
+
def self.run(dispatcher)
|
5
|
+
env = dispatcher.instance_variable_get(:@env)
|
6
|
+
response = env["action_controller.rescue.response"]
|
7
|
+
request = env["action_controller.rescue.request"]
|
8
|
+
request_headers = request.headers.dup
|
9
|
+
request_headers.each do |k, v|
|
10
|
+
case v
|
11
|
+
when String, Fixnum, Numeric
|
12
|
+
else
|
13
|
+
request_headers[k] = "#<#{v.class.name}>"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
agent = ActionController::Base.logger.agent
|
17
|
+
agent.fields.merge!({
|
18
|
+
:env => RAILS_ENV,
|
19
|
+
:response_code => response.status,
|
20
|
+
:request_params => request.request_parameters,
|
21
|
+
:request_headers => request_headers,
|
22
|
+
:response_headers => response.headers
|
23
|
+
})
|
24
|
+
agent.flush
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -6,34 +6,21 @@ class TheAMQPLoggerTest < Test::Unit::TestCase
|
|
6
6
|
@io = StringIO.new
|
7
7
|
end
|
8
8
|
|
9
|
-
test "should be instanciated like a normal logger" do
|
10
|
-
assert_nothing_raised { AMQPLogging::Logger.new(@io) }
|
11
|
-
assert_nothing_raised { AMQPLogging::Logger.new(@io, 2) }
|
12
|
-
assert_nothing_raised { AMQPLogging::Logger.new(@io, 2, 1048576) }
|
13
|
-
end
|
14
|
-
|
15
9
|
test "should be instanciated with an amqp configuration hash" do
|
16
|
-
config = { :queue => "testqueue", :exchange => "testexchange", :host => "testhost", :shift_age => 4, :shift_size => 1338, :routing_key => "foobar" }
|
17
|
-
AMQPLogging::
|
18
|
-
|
19
|
-
logger = AMQPLogging::Logger.new(@io, config)
|
20
|
-
end
|
10
|
+
config = { :queue => "testqueue", :exchange => "testexchange", :host => "testhost", :shift_age => 4, :shift_size => 1338, :routing_key => "foobar", :exchange_durable => true, :exchange_auto_delete => false, :exchange_type => :topic }
|
11
|
+
AMQPLogging::LogDevice.expects(:new).with(config).returns(stub_everything)
|
21
12
|
|
22
|
-
|
23
|
-
AMQPLogging::AMQPLogDevice.any_instance.stubs(:exchange).returns(stub_everything('test_exchange'))
|
24
|
-
logger = AMQPLogging::Logger.new(@io)
|
25
|
-
logger.debug "logging something"
|
26
|
-
assert_match "logging something", @io.string
|
13
|
+
logger = AMQPLogging::Logger.new(config)
|
27
14
|
end
|
28
15
|
|
29
16
|
test "should pause AMQP logging if exceptions during logging occure" do
|
30
17
|
# in case you ask why not just using mocha expectations here: the rescue in the tested code also rescues the mocha exception
|
31
18
|
# this fake exchange object increases a counter everytime publish is executed so we can check the number of executions
|
32
19
|
class TestExchange; attr_reader :counter; def publish(*args); @counter ||= 0; @counter += 1; raise 'Foo'; end; end
|
33
|
-
logger = AMQPLogging::Logger.new
|
20
|
+
logger = AMQPLogging::Logger.new
|
34
21
|
exchange = TestExchange.new
|
35
|
-
AMQPLogging::
|
36
|
-
AMQPLogging::
|
22
|
+
AMQPLogging::LogDevice.any_instance.stubs(:exchange).returns(exchange)
|
23
|
+
AMQPLogging::LogDevice.any_instance.stubs(:bunny).returns(stub_everything("bunny stub"))
|
37
24
|
2.times { logger.debug "This will raise" }
|
38
25
|
|
39
26
|
assert_equal 1, exchange.counter
|
@@ -46,8 +33,8 @@ class TheAMQPLoggerTest < Test::Unit::TestCase
|
|
46
33
|
errback = lambda {|exception| begin raise exception; rescue FooBarException; @called = true; end}
|
47
34
|
raising_exchange = mock("mocked exchange")
|
48
35
|
raising_exchange.expects(:publish).raises(FooBarException)
|
49
|
-
AMQPLogging::
|
50
|
-
logger = AMQPLogging::Logger.new
|
36
|
+
AMQPLogging::LogDevice.any_instance.stubs(:exchange).returns(raising_exchange)
|
37
|
+
logger = AMQPLogging::Logger.new
|
51
38
|
logger.errback = errback
|
52
39
|
|
53
40
|
assert_nothing_raised do
|
@@ -59,10 +46,10 @@ class TheAMQPLoggerTest < Test::Unit::TestCase
|
|
59
46
|
test "should reset the bunny and exchange instance if a exception occures" do
|
60
47
|
raising_exchange = mock("mocked exchange")
|
61
48
|
raising_exchange.expects(:publish).raises("FFFFFFFFUUUUUUUUUU")
|
62
|
-
AMQPLogging::
|
63
|
-
logger = AMQPLogging::Logger.new
|
49
|
+
AMQPLogging::LogDevice.any_instance.stubs(:exchange).returns(raising_exchange)
|
50
|
+
logger = AMQPLogging::Logger.new
|
64
51
|
|
65
|
-
AMQPLogging::
|
52
|
+
AMQPLogging::LogDevice.any_instance.expects(:reset_amqp)
|
66
53
|
logger.debug("This will raise and send a notification")
|
67
54
|
end
|
68
55
|
|
@@ -73,9 +60,11 @@ class TheLogDeviceTest < Test::Unit::TestCase
|
|
73
60
|
config = { :queue => "testqueue", :exchange => "testexchange", :host => "testhost", :shift_age => 4, :shift_size => 1338 }
|
74
61
|
bunny_stub = stub_everything("bunny_stub")
|
75
62
|
Bunny.expects(:new).with(:host => "testhost").returns(bunny_stub)
|
76
|
-
bunny_stub.expects(:exchange).with(config[:exchange], :
|
63
|
+
bunny_stub.expects(:exchange).with(config[:exchange], :durable => true,
|
64
|
+
:auto_delete => false,
|
65
|
+
:type => :topic).returns(stub("exchange stub", :publish => true))
|
77
66
|
|
78
|
-
logger = AMQPLogging::Logger.new(
|
67
|
+
logger = AMQPLogging::Logger.new(config)
|
79
68
|
logger.debug("foobar")
|
80
69
|
end
|
81
70
|
|
@@ -83,16 +72,16 @@ class TheLogDeviceTest < Test::Unit::TestCase
|
|
83
72
|
message = "some stuff to log"
|
84
73
|
exchange = mock()
|
85
74
|
exchange.expects(:publish).with(anything, :key => "a_routing_key")
|
86
|
-
AMQPLogging::
|
87
|
-
AMQPLogging::Logger.new(
|
75
|
+
AMQPLogging::LogDevice.any_instance.stubs(:exchange).returns(exchange)
|
76
|
+
AMQPLogging::Logger.new(:routing_key => "a_routing_key").debug(message)
|
88
77
|
end
|
89
78
|
|
90
79
|
test "should take a proc argument which gets the logline passed to generate the routing key" do
|
91
80
|
key_generator = lambda {|msg| !!(msg =~ /a message/) }
|
92
81
|
exchange = mock()
|
93
82
|
exchange.expects(:publish).with(anything, :key => "true")
|
94
|
-
AMQPLogging::
|
95
|
-
AMQPLogging::Logger.new(
|
83
|
+
AMQPLogging::LogDevice.any_instance.stubs(:exchange).returns(exchange)
|
84
|
+
AMQPLogging::Logger.new(:routing_key => key_generator).debug("a message")
|
96
85
|
end
|
97
86
|
end
|
98
87
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
|
4
|
+
module AMQPLogging
|
5
|
+
class MetricsAgentTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@agent = MetricsAgent.new
|
8
|
+
@out = StringIO.new
|
9
|
+
@agent.logger = ::Logger.new(@out)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "should record the process id" do
|
13
|
+
assert_equal Process.pid, @agent[:pid]
|
14
|
+
end
|
15
|
+
|
16
|
+
test "should record the hostname" do
|
17
|
+
assert_equal Socket.gethostname.split('.').first, @agent[:host]
|
18
|
+
end
|
19
|
+
|
20
|
+
test "should have convenience methods for accessing the fields" do
|
21
|
+
@agent[:foo] = :bar
|
22
|
+
assert_equal :bar, @agent[:foo]
|
23
|
+
assert_equal @agent[:foo], @agent[:foo]
|
24
|
+
end
|
25
|
+
|
26
|
+
test "should send the collected data as json when flushed" do
|
27
|
+
@agent.flush
|
28
|
+
json = JSON.parse(@out.string)
|
29
|
+
assert_equal Process.pid, json["pid"]
|
30
|
+
end
|
31
|
+
|
32
|
+
test "should reset the collected data when flushed" do
|
33
|
+
@agent[:foo] = :bar
|
34
|
+
@agent.flush
|
35
|
+
assert_equal nil, @agent[:foo]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class LoggingProxyTest < Test::Unit::TestCase
|
40
|
+
def setup
|
41
|
+
@agent = MetricsAgent.new
|
42
|
+
@agent.logger = ::Logger.new('/dev/null')
|
43
|
+
@logger = ::Logger.new('/dev/null')
|
44
|
+
@proxy = @agent.wrap_logger(@logger)
|
45
|
+
end
|
46
|
+
|
47
|
+
test "should return a logger proxy that quaks like a regular logger" do
|
48
|
+
@logger.expects(:debug)
|
49
|
+
@proxy.debug "foobar"
|
50
|
+
end
|
51
|
+
|
52
|
+
test "should register every logline on the agent" do
|
53
|
+
@agent.expects(:add_logline).with(0, nil, "foobar", @logger)
|
54
|
+
@proxy.debug("foobar")
|
55
|
+
end
|
56
|
+
|
57
|
+
test "should take the loglevel of the logger into account" do
|
58
|
+
@logger.level = ::Logger::INFO
|
59
|
+
no_lines_before_logging = @agent[:loglines][:default].size
|
60
|
+
@logger.debug "something"
|
61
|
+
assert_equal no_lines_before_logging, @agent[:loglines][:default].size
|
62
|
+
end
|
63
|
+
|
64
|
+
test "should store the loglines" do
|
65
|
+
assert_equal 0, @agent[:loglines][:default].size
|
66
|
+
@proxy.debug("foobar")
|
67
|
+
assert_equal 1, @agent[:loglines][:default].size
|
68
|
+
end
|
69
|
+
|
70
|
+
test "should store each logline with severity, a timestamp and the message" do
|
71
|
+
some_logline = "asdf0asdf"
|
72
|
+
@proxy.debug "foo"
|
73
|
+
@proxy.warn "bar"
|
74
|
+
@proxy.info some_logline
|
75
|
+
severity, timestamp, message = @agent[:loglines][:default][2]
|
76
|
+
assert_equal Logger::INFO, severity
|
77
|
+
assert_nothing_raised { Time.parse(timestamp) }
|
78
|
+
assert_equal some_logline, message
|
79
|
+
end
|
80
|
+
|
81
|
+
test "should allow to register multiple loggers with different types" do
|
82
|
+
other_logger = ::Logger.new('/dev/null')
|
83
|
+
@agent.wrap_logger(other_logger, :sql)
|
84
|
+
other_logger.info("some fancy stuff here")
|
85
|
+
assert_equal 1, @agent[:loglines][:sql].size
|
86
|
+
end
|
87
|
+
|
88
|
+
test "should reset the collected loglines when flushed" do
|
89
|
+
@proxy.debug "foo"
|
90
|
+
@agent.flush
|
91
|
+
assert_equal [], @agent[:loglines][:default]
|
92
|
+
end
|
93
|
+
|
94
|
+
test "should keep loglines fields for the registered loggers after flushing" do
|
95
|
+
other_logger = ::Logger.new('/dev/null')
|
96
|
+
@agent.wrap_logger(other_logger, :sql)
|
97
|
+
other_logger.info "foo"
|
98
|
+
@agent.flush
|
99
|
+
|
100
|
+
assert_equal [], @agent[:loglines][:sql]
|
101
|
+
end
|
102
|
+
|
103
|
+
test "should remove leading and trailing newlines from the stored loglines" do
|
104
|
+
@proxy.debug "\n\nfoo\n\n"
|
105
|
+
assert_equal "foo", @agent[:loglines][:default][-1][2]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amqp_logging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Pascal Friederich
|
@@ -51,9 +51,25 @@ dependencies:
|
|
51
51
|
type: :runtime
|
52
52
|
version_requirements: *id002
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: json
|
55
55
|
prerelease: false
|
56
56
|
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 7
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 4
|
65
|
+
- 0
|
66
|
+
version: 1.4.0
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
57
73
|
none: false
|
58
74
|
requirements:
|
59
75
|
- - ">="
|
@@ -63,7 +79,21 @@ dependencies:
|
|
63
79
|
- 0
|
64
80
|
version: "0"
|
65
81
|
type: :development
|
66
|
-
version_requirements: *
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: redgreen
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
67
97
|
description:
|
68
98
|
email:
|
69
99
|
- pascal.friederich@xing.com
|
@@ -74,9 +104,15 @@ extensions: []
|
|
74
104
|
extra_rdoc_files:
|
75
105
|
- Readme.rdoc
|
76
106
|
files:
|
107
|
+
- lib/amqp_logging/log_device.rb
|
108
|
+
- lib/amqp_logging/logger.rb
|
109
|
+
- lib/amqp_logging/metrics_agent/rails/after_dispatch_callback_handler.rb
|
110
|
+
- lib/amqp_logging/metrics_agent/rails.rb
|
111
|
+
- lib/amqp_logging/metrics_agent.rb
|
77
112
|
- lib/amqp_logging.rb
|
78
113
|
- Readme.rdoc
|
79
|
-
- test/
|
114
|
+
- test/logger_test.rb
|
115
|
+
- test/metrics_agent_test.rb
|
80
116
|
- test/test_helper.rb
|
81
117
|
has_rdoc: true
|
82
118
|
homepage: http://github.com/paukul/amqp_logging
|
@@ -115,5 +151,6 @@ signing_key:
|
|
115
151
|
specification_version: 3
|
116
152
|
summary: A ruby logger class that logs to an AMQP exchange in addition to your default log device.
|
117
153
|
test_files:
|
118
|
-
- test/
|
154
|
+
- test/logger_test.rb
|
155
|
+
- test/metrics_agent_test.rb
|
119
156
|
- test/test_helper.rb
|