README.md in loggr-1.0.0 vs README.md in loggr-1.1.0
- old
+ new
@@ -3,45 +3,44 @@
Loggr provides a factory for creating logger instances. It:
- Provides a wrapper for SLF4J (on JRuby)
- Has adapters for Stdlib Logger and ActiveSupport::BufferedLogger
-- Supports Rails
+- Supports Rails, including the new `tagged` method
- Handles using a Mapped Diagnostic Context (MDC)
**So, why should I use a logger factory instead of just `Logger.new('out.log')`
or `Rails.logger`?** Deploying the same application to different environments,
with different logging requirements, might make such a step necessary. Or when
trying to take advante of SLF4J-only features like the MDC or markers.
Information
-----------
-**Wiki** The Loggr Wiki will hopefully soon be filled with how-to articles and
-frequently asked questions.
-
-https://github.com/at-point/loggr/wiki
-
**Bug Reports** No software is without bugs, if you discover a problem please report the issue.
https://github.com/at-point/loggr/issues
**Documentation** The latest RDocs are available on rubydoc.info.
http://rubydoc.info/github/at-point/loggr/master/frames
+**Gem** The latest stable gem is always on rubygems.org.
+
+http://rubygems.org/gems/loggr
+
Installation
------------
Like any gem, just add it to the Gemfile:
# stable version
- gem 'loggr', '~> 1.0.0'
-
+ gem 'loggr', '~> 1.1.0'
+
# latest development version
gem 'loggr', :git => 'git://github.com/at-point/loggr.git'
-
+
Getting started
===============
Guides through creating some sample Logger instances and integration with Rails, if interested
in using the SLF4J Logger directly (without the factory), skip to _The SLF4J Wrapper_.
@@ -56,11 +55,11 @@
Once an adapter has been specified new logger instances can be easily created using:
def logger
@logger ||= LoggerFactory.logger 'my.app.SomeClass', :marker => "WORKER"
end
-
+
The adapter then handles creating new logger instances and uses features like the ability
to set markers (if supported), or setting a logger name. Based on the adapter a new logger
will be returned with all things defined like the marker, or a custom logger name - if the
adapter supports it, else it just tries to return a logger which has an API compatible with
those found by Stdlibs Logger.
@@ -74,101 +73,105 @@
### Creating new loggers
LoggerFactory.logger 'app' # create a logger with named app
LoggerFactory.logger 'app', :to => 'debug.out' # write to debug.out
LoggerFactory.logger 'app', :to => $stderr # write to stderr
-
+
**Note:** not all adapters support all options, so some adapters might just ignore certain
options, but this is intended :)
### Bundled Adapters
-**<code>LoggerFactory.adapter = :base</code>**
+**LoggerFactory.adapter = :base**
The base adapter creates ruby stdlib `Logger` instances. Supported options for
`LoggerFactory.logger(name, options = {})` are:
- `:to`, String or IO, where to log should be written to (default: `"#{name}.log"`)
-- `:level`, Fixnum, one of `Logger::Severity`, the minimum severity to log (default: `Logger::Severity::DEBUG`)
+- `:level`, Fixnum, one of `Logger::Severity`, the minimum severity to log (default: `Logger::Severity::INFO`)
-**<code>LoggerFactory.adapter = :buffered</code>**
+**LoggerFactory.adapter = :buffered**
Creates `ActiveSupport::BufferedLogger` instances and supports the same options
as the `:base` adapter.
-**<code>LoggerFactory.adapter = :rails</code>**
+**LoggerFactory.adapter = :rails**
This adapter alwasy returns the `Rails.logger`, which is very useful e.g. in development
environments or testing, where we just care that it's somewhere in our `logs/development.log`.
*Note:* Rails is automatically detected and the rails adapter is the default adapter when
`::Rails` is present - else the base adapter is the default.
-**<code>LoggerFactory.adapter = :slf4j</code>**
+**LoggerFactory.adapter = :slf4j**
SLF4J only works with JRuby (because it's Java) and you are responsible for a) having an
SLF4J implementation on the classpath and it's configuration. Furthermore slf4j supports
these options for `LoggerFactory.logger(name, options = {})`:
- `:marker`, String, additional marker logged with each statement (default: `nil`)
### Using the Mapped Diagnostic Context (MDC)
-Some loggers provide a MDC (or mapped diagnostic context), which can be used to annotate
-log outputs with additional bits of information. At the moment only SLF4J really can make
+Some loggers provide a MDC (or mapped diagnostic context) or a similar feature like
+ActiveSupport 3.2s `TaggedLogging#tagged` which can be used to annotate log outputs with
+additional bits of information. At the moment only SLF4J and ActiveSupport 3.2 can make
use of this. Though, to provide a clean and consistent API all adapters _must_ provide
-access to an MDC, so the MDC can be used in code no matter the adapter, a sample use case
-(in Rails):
+access to an MDC and each logger _must_ respond to both `tagged` and `mapped`, so these
+features can be used in code no matter the adapter.
+A sample use case:
+
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_filter :push_ip_to_mdc
-
- private
+ def logger; @logger ||= LoggerFactory.logger('sample') end
+
+ private
def push_ip_to_mdc
- LoggerFactory.mdc[:ip] = request.ip
- yield
- ensure
- LoggerFactory.mdc.delete(:ip)
+ logger.mapped(:ip => request.ip) do
+ yield
+ end
end
end
-
+
When using SLF4J all statements would now be annotated with the IP from where the request
-was made from.
+was made from, when using Rails 3.2 it would use it's support for `tagged` and add a tag
+named `ip=....`.
-The _SLF4J_ Wrapper
-===================
+The SLF4J Wrapper
+=================
Apart from the logger factory, this gem provides a ruby wrapper for logging using SLF4J and
taking advantage of:
-- Same API as exposed by Stdlib Logger or AS::BufferedLogger
+- Same API as exposed by Stdlib Logger, AS::BufferedLogger and AS::TaggedLogging
- SLF4J markers
- The Mapped Diagnostic Context (MDC)
-- Access to SLF4J & Logback implementation JARs
+- Access to SLF4J & Logback implementation JARs
The Logger
----------
Creating a new logger is as simple as creating instances of `Loggr::SLF4J::Logger`:
# logger named "my.package.App"
@logger = Loggr::SLF4J::Logger.new 'my.package.App'
-
+
# logger named "some.sample.Application" => classes are converted to java notation
@logger = Loggr::SLF4J::Logger.new Some::Sample::Application
-
+
# logger with a default marker named "APP"
@logger = Loggr::SLF4J::Logger.new 'my.package.App', :marker => 'APP'
-
+
Logging events is like using Stdlib Logger:
# log with level INFO
@logger.info "some info message"
-
+
# log with level DEBUG, if enabled
@logger.debug "verbose information" if @logger.debug?
-
+
# log with level DEBUG and marker "QUEUE" (masking as progname)
@logger.debug "do something", "QUEUE"
The MDC
-------
@@ -185,33 +188,54 @@
It's a good practice to wrap MDC set/get into begin/ensure blocks to ensure the value
is cleared afterwards, even in case of errors. The user is responsible for getting rid
of these values. To just clear all values use `Loggr::SLF4J::MDC.clear`
+Tagging and mapping
+-------------------
+
+As an alternative to using the MDC directly, each logger exposes a method which is more
+ruby-like than setting the MDC and having to handle the `ensure` all by itself.
+
+ logger.mapped(:user => username) do
+ do_some_stuff
+ end
+
+This ensures that the key is cleared at the end, these calls can also be easily nested.
+Starting with ActiveSupport 3.2 there's support for `TaggedLogging`, SLF4J mimics this
+behaviour by using the mapped diagnostic context:
+
+ logger.tagged("some", "values") do
+ do_some_stuff
+ end
+
+Within the block the MDC has been assigned `some, values` to the key `:tags`. Nested values
+are just appended to this key.
+
Extending & Contributing
========================
Of course any custom adapters (e.g. for log4r or the logging gem) are greatly appreciated.
To write a custom adapter just do something like:
- class MyModule::MyCustomAdapter < Loggr::Adpater::AbstractAdapter
+ class MyModule::MyCustomAdapter < Loggr::Adpater::AbstractAdapter
def logger(name, options = {})
# build logger instances and return it
- end
+ end
end
-
+
# use custom adapter
LoggerFactory.adapter = MyModule::MyCustomAdapter.new
-
+
Extending from `Loggr::Adapter::AbstractAdapter` provides the adapter with a default
MDC implementation (backed by a hash stored in a thread local).
Similar to ActiveModel there are also Lint tests available to verify if your adapter
and the logger and mdc returned adhere to the API.
class MyModule::MyCustomAdapterTest < Test::Unit::TestCase
include Loggr::Lint::Tests
-
+
def setup
# required, so the Lint Tests can pick it up
@adapter = MyModule::MyCustomAdapter.new
end
end