loggr 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +12 -0
- data/Appraisals +11 -0
- data/README.md +66 -42
- data/Rakefile +5 -3
- data/gemfiles/activesupport-3.0.x.gemfile +7 -0
- data/gemfiles/activesupport-3.1.gemfile +7 -0
- data/gemfiles/activesupport-head.gemfile +7 -0
- data/lib/loggr.rb +8 -8
- data/lib/loggr/adapter.rb +3 -2
- data/lib/loggr/adapter/abstract.rb +5 -5
- data/lib/loggr/adapter/base.rb +11 -10
- data/lib/loggr/adapter/buffered.rb +6 -4
- data/lib/loggr/adapter/nop.rb +10 -6
- data/lib/loggr/lint.rb +35 -21
- data/lib/loggr/slf4j/logger.rb +39 -16
- data/lib/loggr/slf4j/mdc.rb +9 -9
- data/lib/loggr/support/annotations.rb +40 -0
- data/lib/loggr/version.rb +2 -1
- data/loggr.gemspec +4 -2
- data/test/test_helper.rb +16 -10
- data/test/unit/adapter/base_test.rb +14 -14
- data/test/unit/adapter/buffered_test.rb +11 -11
- data/test/unit/adapter/rails_test.rb +4 -6
- data/test/unit/factory_test.rb +26 -11
- data/test/unit/slf4j/logger_test.rb +62 -26
- metadata +112 -109
@@ -1,24 +1,26 @@
|
|
1
1
|
require 'loggr/adapter/base'
|
2
|
+
require 'loggr/support/annotations'
|
2
3
|
require 'active_support/buffered_logger'
|
3
4
|
|
4
5
|
module Loggr
|
5
6
|
module Adapter
|
6
|
-
|
7
|
+
|
7
8
|
# Backend for `ActiveSupport::BufferedLogger`.
|
8
9
|
#
|
9
10
|
class BufferedAdapter < BaseAdapter
|
10
|
-
|
11
|
+
|
11
12
|
protected
|
12
13
|
# Creates a new `AS::BufferedLogger` instance, note that BufferedLogger has
|
13
14
|
# no support for setting a default progname, so `name` is basically ignored.
|
14
15
|
#
|
15
16
|
def build_new_logger(name, options = {})
|
16
|
-
ActiveSupport::BufferedLogger.new(options[:to] || "#{name.to_s.gsub(/[\s\/]+/, '_')}.log").tap do |logger|
|
17
|
+
logger = ActiveSupport::BufferedLogger.new(options[:to] || "#{name.to_s.gsub(/[\s\/]+/, '_')}.log").tap do |logger|
|
17
18
|
logger.level = options[:level] || ActiveSupport::BufferedLogger::INFO
|
18
19
|
end
|
20
|
+
Loggr::Support::Annotations.enhance(logger)
|
19
21
|
end
|
20
22
|
end
|
21
|
-
|
23
|
+
|
22
24
|
# THE instance
|
23
25
|
Buffered = BufferedAdapter.new
|
24
26
|
end
|
data/lib/loggr/adapter/nop.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'loggr/adapter/abstract'
|
2
|
+
require 'loggr/support/annotations'
|
2
3
|
|
3
4
|
module Loggr
|
4
5
|
module Adapter
|
@@ -6,14 +7,17 @@ module Loggr
|
|
6
7
|
# Silences all logging operations, nothing is written at all.
|
7
8
|
#
|
8
9
|
class NOPAdapter < AbstractAdapter
|
9
|
-
|
10
|
+
|
10
11
|
class NOPLogger
|
11
12
|
# Has no impact anyway :)
|
12
13
|
attr_accessor :level
|
13
|
-
|
14
|
+
|
14
15
|
# Just to ensure compatiability with AS::BufferedLogger
|
15
16
|
attr_reader :auto_flushing, :flush, :close
|
16
|
-
|
17
|
+
|
18
|
+
# Support fuer Annotations wie `tagged` und `mapped`.
|
19
|
+
include Loggr::Support::Annotations::NOPSupport
|
20
|
+
|
17
21
|
# Yields empty implementations for all severities
|
18
22
|
%w{trace debug info warn error fatal}.each do |severity|
|
19
23
|
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
@@ -26,13 +30,13 @@ module Loggr
|
|
26
30
|
EOT
|
27
31
|
end
|
28
32
|
end
|
29
|
-
|
33
|
+
|
30
34
|
# Get single NOPLogger instance
|
31
35
|
def logger(name, options = {})
|
32
36
|
@logger ||= NOPLogger.new
|
33
|
-
end
|
37
|
+
end
|
34
38
|
end
|
35
|
-
|
39
|
+
|
36
40
|
# THE instance
|
37
41
|
NOP = NOPAdapter.new
|
38
42
|
end
|
data/lib/loggr/lint.rb
CHANGED
@@ -2,27 +2,51 @@ require 'tempfile'
|
|
2
2
|
|
3
3
|
module Loggr
|
4
4
|
module Lint
|
5
|
-
|
5
|
+
|
6
6
|
# == Adapter and Logger Lint Tests
|
7
7
|
#
|
8
8
|
# You can test whether an object provides a compliant adapter and logger
|
9
9
|
# by including <tt>Logger::Lint::Tests</tt>
|
10
10
|
# in your tests.
|
11
11
|
#
|
12
|
-
# Ensure you set the instance variable <tt>@adapter</tt> to
|
12
|
+
# Ensure you set the instance variable <tt>@adapter</tt> to the adapter to lint.
|
13
13
|
#
|
14
14
|
module Tests
|
15
|
+
|
16
|
+
# Verifies `adapter#logger`, it checks that:
|
17
|
+
#
|
18
|
+
# - the factory method named #logger exists
|
19
|
+
# - that it accepts two arguments, name and options hash
|
20
|
+
# - the returned instnace responds to debug, info, warn, error and fatal
|
21
|
+
# - responds to tagged and mapped
|
22
|
+
#
|
15
23
|
def test_adapter_logger
|
16
24
|
assert adapter.respond_to?(:logger), "The adapter should respond to #logger"
|
17
|
-
assert adapter.method('logger').arity == -2, "The adapter should accept two parameters for #logger, name and options hash"
|
25
|
+
assert adapter.method('logger').arity == -2, "The adapter should accept two parameters for #logger, name and options hash"
|
26
|
+
|
27
|
+
@tempfile = Tempfile.new('lint')
|
28
|
+
logger = adapter.logger('lint', :to => @tempfile.path)
|
29
|
+
%w{debug info warn error fatal}.each do |level|
|
30
|
+
assert logger.respond_to?(level), "The logger should respond to ##{level}"
|
31
|
+
#assert logger.respond_to?("#{level}?"), "The logger should respond to ##{level}?"
|
32
|
+
end
|
33
|
+
|
34
|
+
assert logger.respond_to?(:tagged), "The logger should respond to #tagged"
|
35
|
+
assert logger.respond_to?(:mapped), "The logger should respond to #mapped"
|
36
|
+
ensure
|
37
|
+
@tempfile.unlink if @tempfile
|
18
38
|
end
|
19
|
-
|
39
|
+
|
40
|
+
# Verifies `adapter#mdc`, it checks that:
|
41
|
+
#
|
42
|
+
# - the factory method named #mdc exists
|
43
|
+
# - it accepts no arguments
|
44
|
+
# - the returned mdc responds to []=, [], delete, clear and to_hash
|
45
|
+
#
|
20
46
|
def test_adapter_mdc
|
21
47
|
assert adapter.respond_to?(:mdc), "The adapter should respond to #mdc"
|
22
48
|
assert adapter.method('mdc').arity == 0, "The adapter should accept no parameters for #mdc"
|
23
|
-
|
24
|
-
|
25
|
-
def test_mdc_methods
|
49
|
+
|
26
50
|
mdc = adapter.mdc
|
27
51
|
assert mdc.respond_to?(:[]=), "The mdc should respond to #[]="
|
28
52
|
assert mdc.respond_to?(:[]), "The mdc should respond to #[]"
|
@@ -30,21 +54,11 @@ module Loggr
|
|
30
54
|
assert mdc.respond_to?(:clear), "The mdc should respond to #clear"
|
31
55
|
assert mdc.respond_to?(:to_hash), "The mdc should respond to #to_hash"
|
32
56
|
end
|
33
|
-
|
34
|
-
def test_logger_methods
|
35
|
-
tempfile = Tempfile.new('lint')
|
36
|
-
logger = adapter.logger('lint', :to => tempfile.path)
|
37
|
-
%w{debug info warn error fatal}.each do |level|
|
38
|
-
assert logger.respond_to?(level), "The logger should respond to ##{level}"
|
39
|
-
assert logger.respond_to?("#{level}?"), "The logger should respond to ##{level}?"
|
40
|
-
end
|
41
|
-
ensure
|
42
|
-
tempfile.unlink
|
43
|
-
end
|
44
|
-
|
57
|
+
|
45
58
|
protected
|
46
|
-
|
47
|
-
# Access the adapter, defined
|
59
|
+
|
60
|
+
# Access the adapter, must be defined <tt>@adapter</tt> in the
|
61
|
+
# `setup` method.
|
48
62
|
def adapter
|
49
63
|
assert !!@adapter, "An adapter must be defined"
|
50
64
|
@adapter
|
data/lib/loggr/slf4j/logger.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'loggr/severity'
|
2
|
+
require 'loggr/slf4j/mdc'
|
2
3
|
|
3
4
|
module Loggr
|
4
5
|
module SLF4J
|
5
|
-
|
6
|
+
|
6
7
|
# Simple marker factory which uses `org.slf4j.MarkerFactory`, but
|
7
8
|
# caches the result in a local ruby hash, by name.
|
8
9
|
class MarkerFactory
|
9
|
-
|
10
|
+
|
10
11
|
# Get marker for any non-empty string.
|
11
12
|
def self.[](name)
|
12
13
|
name = name.to_s.strip
|
@@ -15,35 +16,36 @@ module Loggr
|
|
15
16
|
@markers[name] ||= Java::OrgSlf4j::MarkerFactory.getMarker(name)
|
16
17
|
end
|
17
18
|
end
|
18
|
-
|
19
|
+
|
19
20
|
# A logger which is backed by SLF4J, thus only useable in a JRuby environment.
|
20
21
|
#
|
21
22
|
class Logger
|
22
|
-
|
23
|
+
|
23
24
|
# Get severities
|
24
25
|
include Loggr::Severity
|
25
|
-
|
26
|
+
|
26
27
|
# Basically has *no* impact, because is handled by SLF4J
|
27
28
|
attr_accessor :level
|
28
|
-
|
29
|
+
|
29
30
|
# Just to ensure compatiability with AS::BufferedLogger
|
30
31
|
attr_reader :auto_flushing, :flush, :close
|
31
|
-
|
32
|
+
|
32
33
|
# Access raw SLF4J logger & marker instances
|
33
|
-
attr_reader :java_logger, :java_marker
|
34
|
-
|
34
|
+
attr_reader :java_logger, :java_marker, :java_mdc
|
35
|
+
|
35
36
|
# Create a new Logger instance for the given name
|
36
37
|
#
|
37
38
|
def initialize(name, options = {})
|
38
39
|
name = self.class.in_java_notation(name)
|
39
40
|
@java_logger = Java::OrgSlf4j::LoggerFactory.getLogger(name.to_s)
|
40
41
|
@java_marker = MarkerFactory[options[:marker]]
|
41
|
-
|
42
|
+
@java_mdc = options[:mdc] || Loggr::SLF4J::MDC
|
43
|
+
|
42
44
|
# seriously, this is handled by slf4j and pretty dynamic
|
43
45
|
@level = Logger::UNKNOWN
|
44
46
|
@auto_flushing = true
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
# Create the logger methods via meta programming, sweet.
|
48
50
|
%w{trace debug info warn error}.each do |severity|
|
49
51
|
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
@@ -53,7 +55,7 @@ module Loggr
|
|
53
55
|
java_logger.#{severity}(marker, build_message(message, progname, &block)) # java_logger.debug(marker, build_message(message, progname, &block))
|
54
56
|
end # end
|
55
57
|
end # end
|
56
|
-
|
58
|
+
|
57
59
|
def #{severity}? # def debug?
|
58
60
|
!!java_logger.is_#{severity}_enabled(java_marker) # !!java_logger.is_debug_enabled(java_marker)
|
59
61
|
end # end
|
@@ -63,7 +65,28 @@ module Loggr
|
|
63
65
|
# Add support for fatal, just alias to error
|
64
66
|
alias_method :fatal, :error
|
65
67
|
alias_method :fatal?, :error?
|
66
|
-
|
68
|
+
|
69
|
+
# Uses the mapped diagnostic context to add tags, like
|
70
|
+
# ActiveSupport 3.2's TaggedLogger.
|
71
|
+
#
|
72
|
+
def tagged(*new_tags)
|
73
|
+
old_tags = java_mdc[:tags].to_s
|
74
|
+
java_mdc[:tags] = (old_tags.split(', ') + new_tags.flatten).join(', ')
|
75
|
+
yield
|
76
|
+
ensure
|
77
|
+
java_mdc[:tags] = old_tags.length == 0 ? nil : old_tags
|
78
|
+
end
|
79
|
+
|
80
|
+
# A more describtive alternative to tagged is mapped, which just makes
|
81
|
+
# use of the MDC directly, basically.
|
82
|
+
def mapped(hash = {})
|
83
|
+
old_keys = hash.keys.inject({}) { |hsh,k| hsh[k] = java_mdc[k]; hsh }
|
84
|
+
hash.each { |key, value| java_mdc[key] = value }
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
old_keys.each { |key, value| java_mdc[key] = value }
|
88
|
+
end
|
89
|
+
|
67
90
|
# If a class, module or object is used converts `Foo::Bar::SomeThing` to
|
68
91
|
# java notation: `foo.bar.SomeThing`. Symbols and Strings are left as is!
|
69
92
|
#
|
@@ -74,15 +97,15 @@ module Loggr
|
|
74
97
|
last = parts.pop
|
75
98
|
parts.map { |p| p.downcase }.push(last).join('.')
|
76
99
|
end
|
77
|
-
|
100
|
+
|
78
101
|
protected
|
79
|
-
|
102
|
+
|
80
103
|
# Construct the message, note that progname will be ignored, maybe set as
|
81
104
|
# MDC?
|
82
105
|
def build_message(message = nil, progname = nil, &block)
|
83
106
|
message = yield if message.nil? && block_given?
|
84
107
|
message.to_s.gsub(/$\s*^/, '')
|
85
|
-
end
|
108
|
+
end
|
86
109
|
end
|
87
110
|
end
|
88
111
|
end
|
data/lib/loggr/slf4j/mdc.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Loggr
|
2
2
|
module SLF4J
|
3
|
-
|
3
|
+
|
4
4
|
# Wrapper around the SLF4J MDC.
|
5
5
|
#
|
6
6
|
class MDCWrapper
|
7
|
-
|
7
|
+
|
8
8
|
# Access the original SLF4J MDC
|
9
9
|
attr_accessor :java_mdc
|
10
|
-
|
10
|
+
|
11
11
|
# Create a new SLF4J MDC with the supplied implementation.
|
12
12
|
def initialize(impl = Java::OrgSlf4j::MDC)
|
13
13
|
@java_mdc = impl
|
@@ -15,20 +15,20 @@ module Loggr
|
|
15
15
|
|
16
16
|
# Read a key from the MDC.
|
17
17
|
def [](key); java_mdc.get(key.to_s) end
|
18
|
-
|
18
|
+
|
19
19
|
# Write a value to the MDC.
|
20
|
-
def []=(key, value); java_mdc.put(key.to_s, value.to_s) end
|
21
|
-
|
20
|
+
def []=(key, value); value.nil? ? java_mdc.remove(key.to_s) : java_mdc.put(key.to_s, value.to_s) end
|
21
|
+
|
22
22
|
# Remove a key from the MDC.
|
23
23
|
def delete(key); java_mdc.remove(key.to_s) end
|
24
|
-
|
24
|
+
|
25
25
|
# Clear all keys from the MDC.
|
26
26
|
def clear; java_mdc.clear() end
|
27
|
-
|
27
|
+
|
28
28
|
# Convert MDC to a real hash.
|
29
29
|
def to_hash; java_mdc.getCopyOfContextMap().freeze end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
# An instance is available as MDC :)
|
33
33
|
MDC = MDCWrapper.new
|
34
34
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Loggr
|
2
|
+
|
3
|
+
module Support
|
4
|
+
|
5
|
+
#
|
6
|
+
#
|
7
|
+
module Annotations
|
8
|
+
|
9
|
+
# A doing nothing implementation of tagged & mapped,
|
10
|
+
# just to ensure the methods exist.
|
11
|
+
module NOPSupport
|
12
|
+
def tagged(*args); yield if block_given? end
|
13
|
+
alias_method :mapped, :tagged
|
14
|
+
end
|
15
|
+
|
16
|
+
# Enhances supplied logger with the required features
|
17
|
+
# to handle both `tagged` and `mapped` on the logger itself.
|
18
|
+
#
|
19
|
+
def self.enhance(logger)
|
20
|
+
return ::ActiveSupport::TaggedLogging.new(logger) if defined?(::ActiveSupport::TaggedLogging)
|
21
|
+
logger.send(:extend, NOPSupport)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Try to load AS::TaggedLogging
|
28
|
+
begin
|
29
|
+
require 'active_support/tagged_logging'
|
30
|
+
|
31
|
+
# Enable support for `mapped(:user => "demo")` which falls
|
32
|
+
# back to make use of its `tagged` method.
|
33
|
+
class ::ActiveSupport::TaggedLogging
|
34
|
+
|
35
|
+
# Uses `tagged` to tag items with the hash of infos.
|
36
|
+
def mapped(hash = {}, &block)
|
37
|
+
tagged(hash.map { |h,k| "#{h}=#{k}" }, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue LoadError; end # uhm, ignore we have other ideas ;)
|
data/lib/loggr/version.rb
CHANGED
data/loggr.gemspec
CHANGED
@@ -4,7 +4,7 @@ require "loggr/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "loggr"
|
7
|
-
s.version = Loggr::
|
7
|
+
s.version = Loggr::GEM_VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.summary = 'Logger factory framework (including an SLF4J wrapper)'
|
10
10
|
s.description = 'Adapters for different ruby logging backends. Create loggers using different adapters, like Logger (Stdlib), Rails or SLF4J (in JRuby only).'
|
@@ -20,7 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
21
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
22
|
s.require_path = 'lib'
|
23
|
-
|
23
|
+
|
24
|
+
s.add_development_dependency "rake", ">= 0.8.7"
|
24
25
|
s.add_development_dependency "minitest", ">= 2.3.0"
|
25
26
|
s.add_development_dependency "activesupport", ">= 3.0.0"
|
27
|
+
s.add_development_dependency "appraisal", ">= 0.3.8"
|
26
28
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'minitest/autorun'
|
3
3
|
require 'tempfile'
|
4
|
+
require 'active_support/version'
|
4
5
|
|
5
6
|
# Just to provide some shim ;)
|
6
7
|
module Loggr
|
@@ -11,26 +12,31 @@ module Loggr
|
|
11
12
|
end
|
12
13
|
|
13
14
|
class MiniTest::Unit::TestCase
|
14
|
-
|
15
|
+
|
15
16
|
# Path to root
|
16
17
|
ROOT = File.dirname(File.dirname(__FILE__))
|
17
|
-
|
18
|
-
# Returns `true` if running in java/jruby
|
18
|
+
|
19
|
+
# Returns `true` if running in java/jruby
|
19
20
|
def self.jruby?; !!(RUBY_PLATFORM =~ /java/) end
|
20
|
-
|
21
|
+
|
21
22
|
# Same at instance level
|
22
23
|
def jruby?; self.class.jruby? end
|
23
|
-
|
24
|
+
|
24
25
|
# Yield block if java
|
25
26
|
def if_jruby(&block)
|
26
27
|
yield if block_given? && jruby?
|
27
|
-
end
|
28
|
-
|
28
|
+
end
|
29
|
+
|
29
30
|
# Skip tests, unless using jruby
|
30
31
|
def skip_unless_jruby
|
31
32
|
skip("requires JRuby") unless jruby?
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
# Returns `true` if >= ActiveSupport 3.2 is used
|
36
|
+
def as_3_2?
|
37
|
+
ActiveSupport::VERSION::MAJOR >= 3 && ActiveSupport::VERSION::MINOR >= 2
|
38
|
+
end
|
39
|
+
|
34
40
|
# Yields path to tempfile into block, ensures is cleaned up
|
35
41
|
# afterwards
|
36
42
|
def with_tempfile(name = 'file', &block)
|
@@ -40,11 +46,11 @@ class MiniTest::Unit::TestCase
|
|
40
46
|
ensure
|
41
47
|
tempfile.unlink
|
42
48
|
end
|
43
|
-
|
49
|
+
|
44
50
|
# Ensure all log files are unlinked after block
|
45
51
|
def unlink_log_files(&block)
|
46
52
|
yield if block_given?
|
47
53
|
ensure
|
48
54
|
Dir[File.join(File.dirname(File.dirname(__FILE__))), '*.log'].each { |f| File.unlink(f) if f =~ /\.log$/ }
|
49
|
-
end
|
55
|
+
end
|
50
56
|
end
|