loggr 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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 your adapter.
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
- end
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 by <tt>@adapter</tt>.
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
@@ -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
@@ -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 ;)
@@ -1,3 +1,4 @@
1
1
  module Loggr
2
- VERSION = "1.0.0"
2
+ # Called gem version for compatibility with loggr/agents/loggr-rb (loggr-rb gem)
3
+ GEM_VERSION = "1.1.0"
3
4
  end
@@ -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::VERSION
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
@@ -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