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.
- 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
|