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
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Appraisals
ADDED
data/README.md
CHANGED
@@ -5,7 +5,7 @@ Loggr provides a factory for creating logger instances. It:
|
|
5
5
|
|
6
6
|
- Provides a wrapper for SLF4J (on JRuby)
|
7
7
|
- Has adapters for Stdlib Logger and ActiveSupport::BufferedLogger
|
8
|
-
- Supports Rails
|
8
|
+
- Supports Rails, including the new `tagged` method
|
9
9
|
- Handles using a Mapped Diagnostic Context (MDC)
|
10
10
|
|
11
11
|
**So, why should I use a logger factory instead of just `Logger.new('out.log')`
|
@@ -16,11 +16,6 @@ trying to take advante of SLF4J-only features like the MDC or markers.
|
|
16
16
|
Information
|
17
17
|
-----------
|
18
18
|
|
19
|
-
**Wiki** The Loggr Wiki will hopefully soon be filled with how-to articles and
|
20
|
-
frequently asked questions.
|
21
|
-
|
22
|
-
https://github.com/at-point/loggr/wiki
|
23
|
-
|
24
19
|
**Bug Reports** No software is without bugs, if you discover a problem please report the issue.
|
25
20
|
|
26
21
|
https://github.com/at-point/loggr/issues
|
@@ -29,17 +24,21 @@ https://github.com/at-point/loggr/issues
|
|
29
24
|
|
30
25
|
http://rubydoc.info/github/at-point/loggr/master/frames
|
31
26
|
|
27
|
+
**Gem** The latest stable gem is always on rubygems.org.
|
28
|
+
|
29
|
+
http://rubygems.org/gems/loggr
|
30
|
+
|
32
31
|
Installation
|
33
32
|
------------
|
34
33
|
|
35
34
|
Like any gem, just add it to the Gemfile:
|
36
35
|
|
37
36
|
# stable version
|
38
|
-
gem 'loggr', '~> 1.
|
39
|
-
|
37
|
+
gem 'loggr', '~> 1.1.0'
|
38
|
+
|
40
39
|
# latest development version
|
41
40
|
gem 'loggr', :git => 'git://github.com/at-point/loggr.git'
|
42
|
-
|
41
|
+
|
43
42
|
Getting started
|
44
43
|
===============
|
45
44
|
|
@@ -58,7 +57,7 @@ Once an adapter has been specified new logger instances can be easily created us
|
|
58
57
|
def logger
|
59
58
|
@logger ||= LoggerFactory.logger 'my.app.SomeClass', :marker => "WORKER"
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
The adapter then handles creating new logger instances and uses features like the ability
|
63
62
|
to set markers (if supported), or setting a logger name. Based on the adapter a new logger
|
64
63
|
will be returned with all things defined like the marker, or a custom logger name - if the
|
@@ -76,33 +75,33 @@ defines how logger instances are really created.
|
|
76
75
|
LoggerFactory.logger 'app' # create a logger with named app
|
77
76
|
LoggerFactory.logger 'app', :to => 'debug.out' # write to debug.out
|
78
77
|
LoggerFactory.logger 'app', :to => $stderr # write to stderr
|
79
|
-
|
78
|
+
|
80
79
|
**Note:** not all adapters support all options, so some adapters might just ignore certain
|
81
80
|
options, but this is intended :)
|
82
81
|
|
83
82
|
### Bundled Adapters
|
84
83
|
|
85
|
-
|
84
|
+
**LoggerFactory.adapter = :base**
|
86
85
|
|
87
86
|
The base adapter creates ruby stdlib `Logger` instances. Supported options for
|
88
87
|
`LoggerFactory.logger(name, options = {})` are:
|
89
88
|
|
90
89
|
- `:to`, String or IO, where to log should be written to (default: `"#{name}.log"`)
|
91
|
-
- `:level`, Fixnum, one of `Logger::Severity`, the minimum severity to log (default: `Logger::Severity::
|
90
|
+
- `:level`, Fixnum, one of `Logger::Severity`, the minimum severity to log (default: `Logger::Severity::INFO`)
|
92
91
|
|
93
|
-
|
92
|
+
**LoggerFactory.adapter = :buffered**
|
94
93
|
|
95
94
|
Creates `ActiveSupport::BufferedLogger` instances and supports the same options
|
96
95
|
as the `:base` adapter.
|
97
96
|
|
98
|
-
|
97
|
+
**LoggerFactory.adapter = :rails**
|
99
98
|
|
100
99
|
This adapter alwasy returns the `Rails.logger`, which is very useful e.g. in development
|
101
100
|
environments or testing, where we just care that it's somewhere in our `logs/development.log`.
|
102
101
|
*Note:* Rails is automatically detected and the rails adapter is the default adapter when
|
103
102
|
`::Rails` is present - else the base adapter is the default.
|
104
103
|
|
105
|
-
|
104
|
+
**LoggerFactory.adapter = :slf4j**
|
106
105
|
|
107
106
|
SLF4J only works with JRuby (because it's Java) and you are responsible for a) having an
|
108
107
|
SLF4J implementation on the classpath and it's configuration. Furthermore slf4j supports
|
@@ -112,38 +111,42 @@ these options for `LoggerFactory.logger(name, options = {})`:
|
|
112
111
|
|
113
112
|
### Using the Mapped Diagnostic Context (MDC)
|
114
113
|
|
115
|
-
Some loggers provide a MDC (or mapped diagnostic context)
|
116
|
-
|
114
|
+
Some loggers provide a MDC (or mapped diagnostic context) or a similar feature like
|
115
|
+
ActiveSupport 3.2s `TaggedLogging#tagged` which can be used to annotate log outputs with
|
116
|
+
additional bits of information. At the moment only SLF4J and ActiveSupport 3.2 can make
|
117
117
|
use of this. Though, to provide a clean and consistent API all adapters _must_ provide
|
118
|
-
access to an MDC
|
119
|
-
|
118
|
+
access to an MDC and each logger _must_ respond to both `tagged` and `mapped`, so these
|
119
|
+
features can be used in code no matter the adapter.
|
120
|
+
|
121
|
+
A sample use case:
|
120
122
|
|
121
123
|
# app/controllers/application_controller.rb
|
122
124
|
class ApplicationController < ActionController::Base
|
123
125
|
around_filter :push_ip_to_mdc
|
124
|
-
|
125
|
-
|
126
|
+
def logger; @logger ||= LoggerFactory.logger('sample') end
|
127
|
+
|
128
|
+
private
|
126
129
|
def push_ip_to_mdc
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
LoggerFactory.mdc.delete(:ip)
|
130
|
+
logger.mapped(:ip => request.ip) do
|
131
|
+
yield
|
132
|
+
end
|
131
133
|
end
|
132
134
|
end
|
133
|
-
|
135
|
+
|
134
136
|
When using SLF4J all statements would now be annotated with the IP from where the request
|
135
|
-
was made from.
|
137
|
+
was made from, when using Rails 3.2 it would use it's support for `tagged` and add a tag
|
138
|
+
named `ip=....`.
|
136
139
|
|
137
|
-
The
|
138
|
-
|
140
|
+
The SLF4J Wrapper
|
141
|
+
=================
|
139
142
|
|
140
143
|
Apart from the logger factory, this gem provides a ruby wrapper for logging using SLF4J and
|
141
144
|
taking advantage of:
|
142
145
|
|
143
|
-
- Same API as exposed by Stdlib Logger
|
146
|
+
- Same API as exposed by Stdlib Logger, AS::BufferedLogger and AS::TaggedLogging
|
144
147
|
- SLF4J markers
|
145
148
|
- The Mapped Diagnostic Context (MDC)
|
146
|
-
- Access to SLF4J & Logback implementation JARs
|
149
|
+
- Access to SLF4J & Logback implementation JARs
|
147
150
|
|
148
151
|
The Logger
|
149
152
|
----------
|
@@ -152,21 +155,21 @@ Creating a new logger is as simple as creating instances of `Loggr::SLF4J::Logge
|
|
152
155
|
|
153
156
|
# logger named "my.package.App"
|
154
157
|
@logger = Loggr::SLF4J::Logger.new 'my.package.App'
|
155
|
-
|
158
|
+
|
156
159
|
# logger named "some.sample.Application" => classes are converted to java notation
|
157
160
|
@logger = Loggr::SLF4J::Logger.new Some::Sample::Application
|
158
|
-
|
161
|
+
|
159
162
|
# logger with a default marker named "APP"
|
160
163
|
@logger = Loggr::SLF4J::Logger.new 'my.package.App', :marker => 'APP'
|
161
|
-
|
164
|
+
|
162
165
|
Logging events is like using Stdlib Logger:
|
163
166
|
|
164
167
|
# log with level INFO
|
165
168
|
@logger.info "some info message"
|
166
|
-
|
169
|
+
|
167
170
|
# log with level DEBUG, if enabled
|
168
171
|
@logger.debug "verbose information" if @logger.debug?
|
169
|
-
|
172
|
+
|
170
173
|
# log with level DEBUG and marker "QUEUE" (masking as progname)
|
171
174
|
@logger.debug "do something", "QUEUE"
|
172
175
|
|
@@ -187,21 +190,42 @@ It's a good practice to wrap MDC set/get into begin/ensure blocks to ensure the
|
|
187
190
|
is cleared afterwards, even in case of errors. The user is responsible for getting rid
|
188
191
|
of these values. To just clear all values use `Loggr::SLF4J::MDC.clear`
|
189
192
|
|
193
|
+
Tagging and mapping
|
194
|
+
-------------------
|
195
|
+
|
196
|
+
As an alternative to using the MDC directly, each logger exposes a method which is more
|
197
|
+
ruby-like than setting the MDC and having to handle the `ensure` all by itself.
|
198
|
+
|
199
|
+
logger.mapped(:user => username) do
|
200
|
+
do_some_stuff
|
201
|
+
end
|
202
|
+
|
203
|
+
This ensures that the key is cleared at the end, these calls can also be easily nested.
|
204
|
+
Starting with ActiveSupport 3.2 there's support for `TaggedLogging`, SLF4J mimics this
|
205
|
+
behaviour by using the mapped diagnostic context:
|
206
|
+
|
207
|
+
logger.tagged("some", "values") do
|
208
|
+
do_some_stuff
|
209
|
+
end
|
210
|
+
|
211
|
+
Within the block the MDC has been assigned `some, values` to the key `:tags`. Nested values
|
212
|
+
are just appended to this key.
|
213
|
+
|
190
214
|
Extending & Contributing
|
191
215
|
========================
|
192
216
|
|
193
217
|
Of course any custom adapters (e.g. for log4r or the logging gem) are greatly appreciated.
|
194
218
|
To write a custom adapter just do something like:
|
195
219
|
|
196
|
-
class MyModule::MyCustomAdapter < Loggr::Adpater::AbstractAdapter
|
220
|
+
class MyModule::MyCustomAdapter < Loggr::Adpater::AbstractAdapter
|
197
221
|
def logger(name, options = {})
|
198
222
|
# build logger instances and return it
|
199
|
-
end
|
223
|
+
end
|
200
224
|
end
|
201
|
-
|
225
|
+
|
202
226
|
# use custom adapter
|
203
227
|
LoggerFactory.adapter = MyModule::MyCustomAdapter.new
|
204
|
-
|
228
|
+
|
205
229
|
Extending from `Loggr::Adapter::AbstractAdapter` provides the adapter with a default
|
206
230
|
MDC implementation (backed by a hash stored in a thread local).
|
207
231
|
|
@@ -210,7 +234,7 @@ and the logger and mdc returned adhere to the API.
|
|
210
234
|
|
211
235
|
class MyModule::MyCustomAdapterTest < Test::Unit::TestCase
|
212
236
|
include Loggr::Lint::Tests
|
213
|
-
|
237
|
+
|
214
238
|
def setup
|
215
239
|
# required, so the Lint Tests can pick it up
|
216
240
|
@adapter = MyModule::MyCustomAdapter.new
|
data/Rakefile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'bundler'
|
3
|
+
require 'appraisal'
|
2
4
|
require 'rake/testtask'
|
3
5
|
|
4
|
-
# to fix warnings
|
5
|
-
include Rake::DSL
|
6
|
-
|
7
6
|
Bundler::GemHelper.install_tasks
|
8
7
|
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
9
11
|
desc 'Test the loggr gem.'
|
10
12
|
Rake::TestTask.new(:test) do |t|
|
11
13
|
t.libs << 'test'
|
data/lib/loggr.rb
CHANGED
@@ -8,10 +8,10 @@
|
|
8
8
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
9
|
# permit persons to whom the Software is furnished to do so, subject to
|
10
10
|
# the following conditions:
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# The above copyright notice and this permission notice shall be
|
13
13
|
# included in all copies or substantial portions of the Software.
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
16
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
17
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -22,16 +22,16 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
module Loggr
|
25
|
-
autoload :Adapter, 'loggr/adapter'
|
25
|
+
autoload :Adapter, 'loggr/adapter'
|
26
26
|
autoload :Severity, 'loggr/severity'
|
27
27
|
autoload :Lint, 'loggr/lint'
|
28
|
-
|
28
|
+
|
29
29
|
autoload :SLF4J, 'loggr/slf4j'
|
30
|
-
|
31
|
-
autoload :
|
32
|
-
|
30
|
+
|
31
|
+
autoload :GEM_VERSION, 'loggr/version'
|
32
|
+
|
33
33
|
# Ensure we've got the Log Levels covered
|
34
|
-
include Loggr::Severity
|
34
|
+
include Loggr::Severity
|
35
35
|
end
|
36
36
|
|
37
37
|
# Autoloading the factory into the root.
|
data/lib/loggr/adapter.rb
CHANGED
@@ -85,7 +85,8 @@ module Loggr
|
|
85
85
|
# Try to get adapter class from Symbol, String or use Object as-is.
|
86
86
|
#
|
87
87
|
def get_adapter(adp)
|
88
|
-
|
88
|
+
# okay, this is only because we can't camelize it :)
|
89
|
+
adp = Loggr::Adapter::NOP if !adp
|
89
90
|
|
90
91
|
# Code adapter from ActiveSupport::Inflector#camelize
|
91
92
|
# https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L30
|
@@ -94,7 +95,7 @@ module Loggr
|
|
94
95
|
clazz = adp
|
95
96
|
|
96
97
|
if adp.respond_to?(:to_str)
|
97
|
-
const = begin Loggr::Adapter.const_get(adp.to_s) rescue nil end
|
98
|
+
const = begin Loggr::Adapter.const_get(adp.to_s) rescue begin Loggr::Adapter.const_get(adp.to_s.upcase) rescue nil end end
|
98
99
|
unless const
|
99
100
|
# code adapter from ActiveSupport::Inflector#constantize
|
100
101
|
# https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L107
|
@@ -1,25 +1,25 @@
|
|
1
1
|
module Loggr
|
2
2
|
module Adapter
|
3
|
-
|
3
|
+
|
4
4
|
# A basically abstract base class for logger backend
|
5
5
|
# implementations, provides a default implementation for MDC (hash & thread local based).
|
6
6
|
#
|
7
7
|
# Ensure to implement `#logger`.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
class AbstractAdapter
|
10
|
-
|
10
|
+
|
11
11
|
# Implement which creates a new instance of a logger.
|
12
12
|
def logger(name, options = {})
|
13
13
|
raise "#{self.class.name}#logger is declared `abstract': implement #logger method"
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
# Use a simple thread local hash as fake MDC, because it's
|
17
17
|
# not supported by the logger anyway - but it should be available
|
18
18
|
# for consistency and usage.
|
19
19
|
def mdc
|
20
20
|
mdc_key = "#{self.class.name}.mdc"
|
21
21
|
Thread.current[mdc_key] ||= Hash.new
|
22
|
-
end
|
22
|
+
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/lib/loggr/adapter/base.rb
CHANGED
@@ -1,33 +1,34 @@
|
|
1
1
|
require 'loggr/adapter/abstract'
|
2
|
-
require '
|
2
|
+
require 'loggr/support/annotations'
|
3
3
|
|
4
4
|
module Loggr
|
5
5
|
module Adapter
|
6
|
-
|
6
|
+
|
7
7
|
# Default backend which is backed Rubys Stdlib Logger.
|
8
8
|
#
|
9
9
|
class BaseAdapter < AbstractAdapter
|
10
|
-
|
11
|
-
#
|
10
|
+
|
11
|
+
#
|
12
12
|
#
|
13
13
|
def logger(name, options = {})
|
14
14
|
name = normalize_name(name)
|
15
15
|
@loggers ||= {}
|
16
16
|
@loggers[name] ||= build_new_logger(name, options)
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
protected
|
20
20
|
# Constructs a new logger instance for the supplied options, is called
|
21
21
|
# by `#loggers` when no logger with this name already exists - instead of
|
22
22
|
# creating a new logger...
|
23
23
|
#
|
24
24
|
def build_new_logger(name, options = {})
|
25
|
-
::Logger.new(options[:to] || "#{name.to_s.gsub(/[:\s\/]+/, '_')}.log").tap do |logger|
|
25
|
+
logger = ::Logger.new(options[:to] || "#{name.to_s.gsub(/[:\s\/]+/, '_')}.log").tap do |logger|
|
26
26
|
logger.level = options[:level] || Logger::INFO
|
27
27
|
logger.progname = name
|
28
|
-
end
|
28
|
+
end
|
29
|
+
Loggr::Support::Annotations.enhance(logger)
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
# Because we should also allow using class names, or objects
|
32
33
|
# to construct new loggers (like in SLF4J), it makes sense to
|
33
34
|
# have a method which normalizes some input, be it a string or
|
@@ -45,9 +46,9 @@ module Loggr
|
|
45
46
|
when Module then name.name.to_s
|
46
47
|
else name.class.name.to_s
|
47
48
|
end
|
48
|
-
end
|
49
|
+
end
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
# Okay, basically a singleton thus create instance
|
52
53
|
Base = BaseAdapter.new
|
53
54
|
end
|