loggr 1.0.0 → 1.1.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.
@@ -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