loggr 1.0.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.
@@ -0,0 +1,25 @@
1
+ module Loggr
2
+ module Adapter
3
+
4
+ # A basically abstract base class for logger backend
5
+ # implementations, provides a default implementation for MDC (hash & thread local based).
6
+ #
7
+ # Ensure to implement `#logger`.
8
+ #
9
+ class AbstractAdapter
10
+
11
+ # Implement which creates a new instance of a logger.
12
+ def logger(name, options = {})
13
+ raise "#{self.class.name}#logger is declared `abstract': implement #logger method"
14
+ end
15
+
16
+ # Use a simple thread local hash as fake MDC, because it's
17
+ # not supported by the logger anyway - but it should be available
18
+ # for consistency and usage.
19
+ def mdc
20
+ mdc_key = "#{self.class.name}.mdc"
21
+ Thread.current[mdc_key] ||= Hash.new
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ require 'loggr/adapter/abstract'
2
+ require 'logger'
3
+
4
+ module Loggr
5
+ module Adapter
6
+
7
+ # Default backend which is backed Rubys Stdlib Logger.
8
+ #
9
+ class BaseAdapter < AbstractAdapter
10
+
11
+ #
12
+ #
13
+ def logger(name, options = {})
14
+ name = normalize_name(name)
15
+ @loggers ||= {}
16
+ @loggers[name] ||= build_new_logger(name, options)
17
+ end
18
+
19
+ protected
20
+ # Constructs a new logger instance for the supplied options, is called
21
+ # by `#loggers` when no logger with this name already exists - instead of
22
+ # creating a new logger...
23
+ #
24
+ def build_new_logger(name, options = {})
25
+ ::Logger.new(options[:to] || "#{name.to_s.gsub(/[:\s\/]+/, '_')}.log").tap do |logger|
26
+ logger.level = options[:level] || Logger::INFO
27
+ logger.progname = name
28
+ end
29
+ end
30
+
31
+ # Because we should also allow using class names, or objects
32
+ # to construct new loggers (like in SLF4J), it makes sense to
33
+ # have a method which normalizes some input, be it a string or
34
+ # whatnot to convert to an appropriate name.
35
+ #
36
+ # Subclasses are of course allowed to override this, of course
37
+ # loggers themself are allowed to tweak that name further - this
38
+ # is basically to enable creating loggers from classes/objects
39
+ # for AS::BufferedLoggers and Stdlib Loggers.
40
+ #
41
+ def normalize_name(name)
42
+ return name.to_str if name.respond_to?(:to_str)
43
+ case name
44
+ when Symbol then name.to_s
45
+ when Module then name.name.to_s
46
+ else name.class.name.to_s
47
+ end
48
+ end
49
+ end
50
+
51
+ # Okay, basically a singleton thus create instance
52
+ Base = BaseAdapter.new
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ require 'loggr/adapter/base'
2
+ require 'active_support/buffered_logger'
3
+
4
+ module Loggr
5
+ module Adapter
6
+
7
+ # Backend for `ActiveSupport::BufferedLogger`.
8
+ #
9
+ class BufferedAdapter < BaseAdapter
10
+
11
+ protected
12
+ # Creates a new `AS::BufferedLogger` instance, note that BufferedLogger has
13
+ # no support for setting a default progname, so `name` is basically ignored.
14
+ #
15
+ def build_new_logger(name, options = {})
16
+ ActiveSupport::BufferedLogger.new(options[:to] || "#{name.to_s.gsub(/[\s\/]+/, '_')}.log").tap do |logger|
17
+ logger.level = options[:level] || ActiveSupport::BufferedLogger::INFO
18
+ end
19
+ end
20
+ end
21
+
22
+ # THE instance
23
+ Buffered = BufferedAdapter.new
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ require 'loggr/adapter/abstract'
2
+
3
+ module Loggr
4
+ module Adapter
5
+
6
+ # Silences all logging operations, nothing is written at all.
7
+ #
8
+ class NOPAdapter < AbstractAdapter
9
+
10
+ class NOPLogger
11
+ # Has no impact anyway :)
12
+ attr_accessor :level
13
+
14
+ # Just to ensure compatiability with AS::BufferedLogger
15
+ attr_reader :auto_flushing, :flush, :close
16
+
17
+ # Yields empty implementations for all severities
18
+ %w{trace debug info warn error fatal}.each do |severity|
19
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
20
+ def #{severity}(*args, &block) # def debug(*args, &block)
21
+ end # end
22
+
23
+ def #{severity}? # def debug?
24
+ false # false
25
+ end # end
26
+ EOT
27
+ end
28
+ end
29
+
30
+ # Get single NOPLogger instance
31
+ def logger(name, options = {})
32
+ @logger ||= NOPLogger.new
33
+ end
34
+ end
35
+
36
+ # THE instance
37
+ NOP = NOPAdapter.new
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require 'loggr/adapter/abstract'
2
+
3
+ module Loggr
4
+ module Adapter
5
+
6
+ # Always uses the logger defined on `Rails.logger`, CAVEAT ensure to never
7
+ # ever set `Rails.logger` to this backend, i.e.:
8
+ #
9
+ # # IF YOU DID THIS...
10
+ # Loggr.adapter = Loggr::Adapter::Rails
11
+ #
12
+ # MyApp::Application.configure do
13
+ # # ...NEVER DO THIS !!!
14
+ # config.logger = Loggr.logger('rails')
15
+ # end
16
+ #
17
+ # If you are using the rails adapter, ensure that you do not override `config.logger`
18
+ # with an instance of the Rails adapter logger factory. Keep the default logger, or
19
+ # create a new one using:
20
+ #
21
+ # config.logger = Loggr.logger('rails', :backend => Loggr::Adapter::Buffered)
22
+ #
23
+ class RailsAdapter < AbstractAdapter
24
+
25
+ # The rails backend ignores all options as it just returns
26
+ # always returns `Rails.logger` :)
27
+ #
28
+ def logger(name, options = {})
29
+ ::Rails.logger
30
+ end
31
+ end
32
+
33
+ # THE Rails backed implementation instance
34
+ Rails = RailsAdapter.new
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'loggr/slf4j'
2
+ require 'loggr/adapter/base'
3
+
4
+ module Loggr
5
+ module Adapter
6
+
7
+ # Provides an adapter for the SLF4J Logger.
8
+ #
9
+ class SLF4JAdapter < BaseAdapter
10
+ # Use the SLF4J backed real MDC.
11
+ def mdc
12
+ @mdc ||= Loggr::SLF4J::MDC
13
+ end
14
+
15
+ protected
16
+ # Create a new SLF4JLogger instance.
17
+ def build_new_logger(name, options = {})
18
+ Loggr::SLF4J::Logger.new(name, options)
19
+ end
20
+
21
+ # Uses Logger#in_java_notation on name
22
+ def normalize_name(name)
23
+ Loggr::SLF4J::Logger.in_java_notation(name)
24
+ end
25
+ end
26
+
27
+ # THE instance of it
28
+ SLF4J = SLF4JAdapter.new
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ require 'loggr/adapter'
2
+
3
+ # This is basically a factory facade to create new Logger instances
4
+ # which behave all like standard Ruby Stdlib Loggers.
5
+ #
6
+ class LoggerFactory
7
+ # Ensure we get all those factory methods directly here
8
+ extend Loggr::Adapter
9
+ end
data/lib/loggr/lint.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'tempfile'
2
+
3
+ module Loggr
4
+ module Lint
5
+
6
+ # == Adapter and Logger Lint Tests
7
+ #
8
+ # You can test whether an object provides a compliant adapter and logger
9
+ # by including <tt>Logger::Lint::Tests</tt>
10
+ # in your tests.
11
+ #
12
+ # Ensure you set the instance variable <tt>@adapter</tt> to your adapter.
13
+ #
14
+ module Tests
15
+ def test_adapter_logger
16
+ 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"
18
+ end
19
+
20
+ def test_adapter_mdc
21
+ assert adapter.respond_to?(:mdc), "The adapter should respond to #mdc"
22
+ assert adapter.method('mdc').arity == 0, "The adapter should accept no parameters for #mdc"
23
+ end
24
+
25
+ def test_mdc_methods
26
+ mdc = adapter.mdc
27
+ assert mdc.respond_to?(:[]=), "The mdc should respond to #[]="
28
+ assert mdc.respond_to?(:[]), "The mdc should respond to #[]"
29
+ assert mdc.respond_to?(:delete), "The mdc should respond to #delete"
30
+ assert mdc.respond_to?(:clear), "The mdc should respond to #clear"
31
+ assert mdc.respond_to?(:to_hash), "The mdc should respond to #to_hash"
32
+ 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
+
45
+ protected
46
+
47
+ # Access the adapter, defined by <tt>@adapter</tt>.
48
+ def adapter
49
+ assert !!@adapter, "An adapter must be defined"
50
+ @adapter
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ module Loggr
2
+
3
+ # Exactly the same severities as Ruby Stdlib Logger.
4
+ #
5
+ module Severity
6
+ # Okay, trace is new :)
7
+ TRACE = -1
8
+
9
+ DEBUG = 0
10
+ INFO = 1
11
+ WARN = 2
12
+ ERROR = 3
13
+ FATAL = 4
14
+ UNKNOWN = 5
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module Loggr
2
+ module SLF4J
3
+
4
+ autoload :Logger, 'loggr/slf4j/logger'
5
+ autoload :MDC, 'loggr/slf4j/mdc'
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ module Loggr
2
+ module SLF4J
3
+
4
+ # Simple access to both SLF4J and Logback implementations, for testing
5
+ # and/or warbler integration.
6
+ #
7
+ module Jars
8
+
9
+ # Base dir, where the jar files reside, this is "lib/" ergo "lib/loggr/slf4j/jars.rb/../../../"
10
+ SLF4J_LIB_PATH = File.expand_path(File.dirname(File.dirname(File.dirname(__FILE__))))
11
+
12
+
13
+ # Path to SLF4J API
14
+ def slf4j_api_jar_path
15
+ @api_jar_path ||= Dir[File.join(SLF4J_LIB_PATH, 'slf4j-api-*.jar')].first
16
+ end
17
+ module_function :slf4j_api_jar_path
18
+
19
+ # Logback Core JAR
20
+ def logback_core_jar_path
21
+ @logback_core_jar_path ||= Dir[File.join(SLF4J_LIB_PATH, 'logback-core-*.jar')].first
22
+ end
23
+ module_function :logback_core_jar_path
24
+
25
+ # Logback Classic JAR
26
+ def logback_jar_path
27
+ @logback_jar_path ||= Dir[File.join(SLF4J_LIB_PATH, 'logback-classic-*.jar')].first
28
+ end
29
+ module_function :logback_jar_path
30
+
31
+ # Require all JARs, if `all` is set to `false`, then only
32
+ # the SLF4J API jar is loaded.
33
+ def require_slf4j_jars!(all = true)
34
+ require self.slf4j_api_jar_path
35
+ if all
36
+ require self.logback_core_jar_path
37
+ require self.logback_jar_path
38
+ end
39
+ end
40
+ module_function :require_slf4j_jars!
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,88 @@
1
+ require 'loggr/severity'
2
+
3
+ module Loggr
4
+ module SLF4J
5
+
6
+ # Simple marker factory which uses `org.slf4j.MarkerFactory`, but
7
+ # caches the result in a local ruby hash, by name.
8
+ class MarkerFactory
9
+
10
+ # Get marker for any non-empty string.
11
+ def self.[](name)
12
+ name = name.to_s.strip
13
+ return nil if name.length == 0
14
+ @markers ||= {}
15
+ @markers[name] ||= Java::OrgSlf4j::MarkerFactory.getMarker(name)
16
+ end
17
+ end
18
+
19
+ # A logger which is backed by SLF4J, thus only useable in a JRuby environment.
20
+ #
21
+ class Logger
22
+
23
+ # Get severities
24
+ include Loggr::Severity
25
+
26
+ # Basically has *no* impact, because is handled by SLF4J
27
+ attr_accessor :level
28
+
29
+ # Just to ensure compatiability with AS::BufferedLogger
30
+ attr_reader :auto_flushing, :flush, :close
31
+
32
+ # Access raw SLF4J logger & marker instances
33
+ attr_reader :java_logger, :java_marker
34
+
35
+ # Create a new Logger instance for the given name
36
+ #
37
+ def initialize(name, options = {})
38
+ name = self.class.in_java_notation(name)
39
+ @java_logger = Java::OrgSlf4j::LoggerFactory.getLogger(name.to_s)
40
+ @java_marker = MarkerFactory[options[:marker]]
41
+
42
+ # seriously, this is handled by slf4j and pretty dynamic
43
+ @level = Logger::UNKNOWN
44
+ @auto_flushing = true
45
+ end
46
+
47
+ # Create the logger methods via meta programming, sweet.
48
+ %w{trace debug info warn error}.each do |severity|
49
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
50
+ def #{severity}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
51
+ marker = (progname ? MarkerFactory[progname] : nil) || java_marker # marker = (progname ? MarkerFactory[progname] : nil) || java_marker
52
+ if java_logger.is_#{severity}_enabled(marker) # if java_logger.is_debug_enabled(marker)
53
+ java_logger.#{severity}(marker, build_message(message, progname, &block)) # java_logger.debug(marker, build_message(message, progname, &block))
54
+ end # end
55
+ end # end
56
+
57
+ def #{severity}? # def debug?
58
+ !!java_logger.is_#{severity}_enabled(java_marker) # !!java_logger.is_debug_enabled(java_marker)
59
+ end # end
60
+ EOT
61
+ end
62
+
63
+ # Add support for fatal, just alias to error
64
+ alias_method :fatal, :error
65
+ alias_method :fatal?, :error?
66
+
67
+ # If a class, module or object is used converts `Foo::Bar::SomeThing` to
68
+ # java notation: `foo.bar.SomeThing`. Symbols and Strings are left as is!
69
+ #
70
+ def self.in_java_notation(name)
71
+ return name.to_s if name.respond_to?(:to_str) || name.is_a?(Symbol)
72
+ name = name.is_a?(Module) ? name.name.to_s : name.class.name.to_s
73
+ parts = name.split('::')
74
+ last = parts.pop
75
+ parts.map { |p| p.downcase }.push(last).join('.')
76
+ end
77
+
78
+ protected
79
+
80
+ # Construct the message, note that progname will be ignored, maybe set as
81
+ # MDC?
82
+ def build_message(message = nil, progname = nil, &block)
83
+ message = yield if message.nil? && block_given?
84
+ message.to_s.gsub(/$\s*^/, '')
85
+ end
86
+ end
87
+ end
88
+ end