loggr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in loggr.gemspec
4
+ gemspec
data/LICENSES.md ADDED
@@ -0,0 +1,15 @@
1
+ Simple Logging Facade for Java (SLF4J)
2
+ --------------------------------------
3
+
4
+ - License: http://www.slf4j.org/license.html, MIT license, (c) by QOS.ch
5
+ - File: lib/slf4j-api-1.6.1.jar
6
+ - URL: http://www.slf4j.org/
7
+ - Source: https://github.com/ceki/slf4j
8
+
9
+ Logback
10
+ -------
11
+
12
+ - License: http://logback.qos.ch/license.html, EPL 1.0, (c) by QOS.ch
13
+ - Files: lib/logback-classic-0.9.29.jar, lib/logback-core-0.9.29.jar
14
+ - URL: http://logback.qos.ch/
15
+ - Source: https://github.com/ceki/logback
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Lukas Westermann, at-point ag
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,230 @@
1
+ Loggr
2
+ =====
3
+
4
+ Loggr provides a factory for creating logger instances. It:
5
+
6
+ - Provides a wrapper for SLF4J (on JRuby)
7
+ - Has adapters for Stdlib Logger and ActiveSupport::BufferedLogger
8
+ - Supports Rails
9
+ - Handles using a Mapped Diagnostic Context (MDC)
10
+
11
+ **So, why should I use a logger factory instead of just `Logger.new('out.log')`
12
+ or `Rails.logger`?** Deploying the same application to different environments,
13
+ with different logging requirements, might make such a step necessary. Or when
14
+ trying to take advante of SLF4J-only features like the MDC or markers.
15
+
16
+ Information
17
+ -----------
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
+ **Bug Reports** No software is without bugs, if you discover a problem please report the issue.
25
+
26
+ https://github.com/at-point/loggr/issues
27
+
28
+ **Documentation** The latest RDocs are available on rubydoc.info.
29
+
30
+ http://rubydoc.info/github/at-point/loggr/master/frames
31
+
32
+ Installation
33
+ ------------
34
+
35
+ Like any gem, just add it to the Gemfile:
36
+
37
+ # stable version
38
+ gem 'loggr', '~> 1.0.0'
39
+
40
+ # latest development version
41
+ gem 'loggr', :git => 'git://github.com/at-point/loggr.git'
42
+
43
+ Getting started
44
+ ===============
45
+
46
+ Guides through creating some sample Logger instances and integration with Rails, if interested
47
+ in using the SLF4J Logger directly (without the factory), skip to _The SLF4J Wrapper_.
48
+
49
+ An example, application runs in development on MRI and production in a Jetty container
50
+ (Java/JRuby), lets assume you want to log there SQL using a logger named `jruby.rails.ActiveRecord`.
51
+
52
+ # config/initializers/logging.rb:
53
+ LoggerFactory.adapter = Rails.env.production? ? :slf4j : :rails
54
+ ActiveRecord::Base.logger = LoggerFactory.logger 'jruby.rails.ActiveRecord'
55
+
56
+ Once an adapter has been specified new logger instances can be easily created using:
57
+
58
+ def logger
59
+ @logger ||= LoggerFactory.logger 'my.app.SomeClass', :marker => "WORKER"
60
+ end
61
+
62
+ The adapter then handles creating new logger instances and uses features like the ability
63
+ to set markers (if supported), or setting a logger name. Based on the adapter a new logger
64
+ will be returned with all things defined like the marker, or a custom logger name - if the
65
+ adapter supports it, else it just tries to return a logger which has an API compatible with
66
+ those found by Stdlibs Logger.
67
+
68
+ The LoggerFactory
69
+ ------------------
70
+
71
+ Yap, is responsible for creating new loggers, a logger factory has an adapter, the adapter
72
+ defines how logger instances are really created.
73
+
74
+ ### Creating new loggers
75
+
76
+ LoggerFactory.logger 'app' # create a logger with named app
77
+ LoggerFactory.logger 'app', :to => 'debug.out' # write to debug.out
78
+ LoggerFactory.logger 'app', :to => $stderr # write to stderr
79
+
80
+ **Note:** not all adapters support all options, so some adapters might just ignore certain
81
+ options, but this is intended :)
82
+
83
+ ### Bundled Adapters
84
+
85
+ **<code>LoggerFactory.adapter = :base</code>**
86
+
87
+ The base adapter creates ruby stdlib `Logger` instances. Supported options for
88
+ `LoggerFactory.logger(name, options = {})` are:
89
+
90
+ - `: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::DEBUG`)
92
+
93
+ **<code>LoggerFactory.adapter = :buffered</code>**
94
+
95
+ Creates `ActiveSupport::BufferedLogger` instances and supports the same options
96
+ as the `:base` adapter.
97
+
98
+ **<code>LoggerFactory.adapter = :rails</code>**
99
+
100
+ This adapter alwasy returns the `Rails.logger`, which is very useful e.g. in development
101
+ environments or testing, where we just care that it's somewhere in our `logs/development.log`.
102
+ *Note:* Rails is automatically detected and the rails adapter is the default adapter when
103
+ `::Rails` is present - else the base adapter is the default.
104
+
105
+ **<code>LoggerFactory.adapter = :slf4j</code>**
106
+
107
+ SLF4J only works with JRuby (because it's Java) and you are responsible for a) having an
108
+ SLF4J implementation on the classpath and it's configuration. Furthermore slf4j supports
109
+ these options for `LoggerFactory.logger(name, options = {})`:
110
+
111
+ - `:marker`, String, additional marker logged with each statement (default: `nil`)
112
+
113
+ ### Using the Mapped Diagnostic Context (MDC)
114
+
115
+ Some loggers provide a MDC (or mapped diagnostic context), which can be used to annotate
116
+ log outputs with additional bits of information. At the moment only SLF4J really can make
117
+ use of this. Though, to provide a clean and consistent API all adapters _must_ provide
118
+ access to an MDC, so the MDC can be used in code no matter the adapter, a sample use case
119
+ (in Rails):
120
+
121
+ # app/controllers/application_controller.rb
122
+ class ApplicationController < ActionController::Base
123
+ around_filter :push_ip_to_mdc
124
+
125
+ private
126
+ def push_ip_to_mdc
127
+ LoggerFactory.mdc[:ip] = request.ip
128
+ yield
129
+ ensure
130
+ LoggerFactory.mdc.delete(:ip)
131
+ end
132
+ end
133
+
134
+ When using SLF4J all statements would now be annotated with the IP from where the request
135
+ was made from.
136
+
137
+ The _SLF4J_ Wrapper
138
+ ===================
139
+
140
+ Apart from the logger factory, this gem provides a ruby wrapper for logging using SLF4J and
141
+ taking advantage of:
142
+
143
+ - Same API as exposed by Stdlib Logger or AS::BufferedLogger
144
+ - SLF4J markers
145
+ - The Mapped Diagnostic Context (MDC)
146
+ - Access to SLF4J & Logback implementation JARs
147
+
148
+ The Logger
149
+ ----------
150
+
151
+ Creating a new logger is as simple as creating instances of `Loggr::SLF4J::Logger`:
152
+
153
+ # logger named "my.package.App"
154
+ @logger = Loggr::SLF4J::Logger.new 'my.package.App'
155
+
156
+ # logger named "some.sample.Application" => classes are converted to java notation
157
+ @logger = Loggr::SLF4J::Logger.new Some::Sample::Application
158
+
159
+ # logger with a default marker named "APP"
160
+ @logger = Loggr::SLF4J::Logger.new 'my.package.App', :marker => 'APP'
161
+
162
+ Logging events is like using Stdlib Logger:
163
+
164
+ # log with level INFO
165
+ @logger.info "some info message"
166
+
167
+ # log with level DEBUG, if enabled
168
+ @logger.debug "verbose information" if @logger.debug?
169
+
170
+ # log with level DEBUG and marker "QUEUE" (masking as progname)
171
+ @logger.debug "do something", "QUEUE"
172
+
173
+ The MDC
174
+ -------
175
+
176
+ A wrapper hash for SLF4Js Mapped Diagnostic Context is available using `Loggr::SLF4J::MDC`
177
+ like a hash:
178
+
179
+ begin
180
+ Loggr::SLF4J::MDC[:user] = username
181
+ do_some_stuff
182
+ ensure
183
+ Loggr::SLF4J::MDC.delete(:user)
184
+ end
185
+
186
+ It's a good practice to wrap MDC set/get into begin/ensure blocks to ensure the value
187
+ is cleared afterwards, even in case of errors. The user is responsible for getting rid
188
+ of these values. To just clear all values use `Loggr::SLF4J::MDC.clear`
189
+
190
+ Extending & Contributing
191
+ ========================
192
+
193
+ Of course any custom adapters (e.g. for log4r or the logging gem) are greatly appreciated.
194
+ To write a custom adapter just do something like:
195
+
196
+ class MyModule::MyCustomAdapter < Loggr::Adpater::AbstractAdapter
197
+ def logger(name, options = {})
198
+ # build logger instances and return it
199
+ end
200
+ end
201
+
202
+ # use custom adapter
203
+ LoggerFactory.adapter = MyModule::MyCustomAdapter.new
204
+
205
+ Extending from `Loggr::Adapter::AbstractAdapter` provides the adapter with a default
206
+ MDC implementation (backed by a hash stored in a thread local).
207
+
208
+ Similar to ActiveModel there are also Lint tests available to verify if your adapter
209
+ and the logger and mdc returned adhere to the API.
210
+
211
+ class MyModule::MyCustomAdapterTest < Test::Unit::TestCase
212
+ include Loggr::Lint::Tests
213
+
214
+ def setup
215
+ # required, so the Lint Tests can pick it up
216
+ @adapter = MyModule::MyCustomAdapter.new
217
+ end
218
+ end
219
+
220
+ Contribute
221
+ ----------
222
+
223
+ 1. Fork this project and hack away
224
+ 2. Ensure that the changes are well tested
225
+ 3. Send pull request
226
+
227
+ License & Copyright
228
+ -------------------
229
+
230
+ Loggr is licensed under the MIT License, (c) 2011 by at-point ag.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+
4
+ # to fix warnings
5
+ include Rake::DSL
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ desc 'Test the loggr gem.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
Binary file
Binary file
data/lib/loggr.rb ADDED
@@ -0,0 +1,38 @@
1
+ #--
2
+ # Copyright (c) 2011 Lukas Westermann, at-point ag
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module Loggr
25
+ autoload :Adapter, 'loggr/adapter'
26
+ autoload :Severity, 'loggr/severity'
27
+ autoload :Lint, 'loggr/lint'
28
+
29
+ autoload :SLF4J, 'loggr/slf4j'
30
+
31
+ autoload :VERSION, 'loggr/version'
32
+
33
+ # Ensure we've got the Log Levels covered
34
+ include Loggr::Severity
35
+ end
36
+
37
+ # Autoloading the factory into the root.
38
+ autoload :LoggerFactory, 'loggr/factory'
@@ -0,0 +1,114 @@
1
+ module Loggr
2
+
3
+ # The factory is responsible for getting Logger instances from
4
+ # the adapters.
5
+ #
6
+ # It delegates both the `logger` and `mdc` calls to the current
7
+ # adpater.
8
+ #
9
+ # The adpater can be changed by setting `adpater = ...`:
10
+ #
11
+ # Loggr.adapter = Loggr::Adapter::SLF4J
12
+ #
13
+ # @see #logger, #mdc
14
+ module Adapter
15
+
16
+ # An abstract base class, which provides a simple MDC implementation
17
+ autoload :AbstractAdapter, 'loggr/adapter/abstract'
18
+
19
+ # Adapter for Ruby Stdlib Logger
20
+ autoload :Base, 'loggr/adapter/base'
21
+
22
+ # Adapter for ActiveSupport::BufferedLogger, requires AS
23
+ autoload :Buffered, 'loggr/adapter/buffered'
24
+
25
+ # Adapter which uses Rails.logger
26
+ autoload :Rails, 'loggr/adapter/rails'
27
+
28
+ # Adpater for SLF4J
29
+ autoload :SLF4J, 'loggr/adapter/slf4j'
30
+
31
+ # Get the backend, if no backend is defined uses the default backend.
32
+ #
33
+ # If running in a rails environment, automatically chooses the rails
34
+ # adapter as a default, else base is used.
35
+ def adapter
36
+ @adapter ||= Object.const_defined?(:Rails) ? Loggr::Adapter::Rails : Loggr::Adapter::Base
37
+ end
38
+
39
+ # Set a new adapter, either as string, class or whatever :)
40
+ #
41
+ def adapter=(new_adapter)
42
+ @adapter = get_adapter(new_adapter)
43
+ end
44
+
45
+ # Get a new logger instance for supplied named logger or class name.
46
+ #
47
+ # All adapters must ensure that they provide the same API for creating
48
+ # new loggers. Each logger has a name, further possible options are:
49
+ #
50
+ # - `:to`, filename or IO, where to write the output to
51
+ # - `:level`, Fixnum, starting log level, @see `Loggr::Severity`
52
+ # - `:marker`, String, name of the category/marker
53
+ #
54
+ # If an adapter does not support setting a specific option, just
55
+ # ignore it.
56
+ def logger(name, options = {}, &block)
57
+ use_adapter = options.key?(:adapter) ? get_adapter(options.delete(:adapter)) : self.adapter
58
+ use_adapter.logger(name, options).tap do |logger|
59
+ yield(logger) if block_given?
60
+ end
61
+ end
62
+
63
+ # The Mapped Diagnostic Context is a basically a hash where values
64
+ # can be stored for certain keys, the context must be stored per thread.
65
+ #
66
+ # The most basic MDC implementation is `Thread.local['my_simple_mdc'] ||= Hash.new`.
67
+ #
68
+ # If a adapter provides a native MDC implementation ensure it does expose
69
+ # these methods:
70
+ #
71
+ # - `def []=(key, value)`, set a property in the MDC
72
+ # - `def [](key)`, get a property from the MDC
73
+ # - `def delete(key)`, delete a property in the MDC
74
+ # - `def clear()`, deletes all properties from the MDC
75
+ # - `def to_hash`, access MDC as standard ruby hash (might be clone, though!)
76
+ #
77
+ # Well it should basically behave like a Ruby Hash, eventhough not with all
78
+ # options.
79
+ def mdc
80
+ self.adapter.mdc
81
+ end
82
+
83
+ protected
84
+
85
+ # Try to get adapter class from Symbol, String or use Object as-is.
86
+ #
87
+ def get_adapter(adp)
88
+ adp = Loggr::Adapter::SLF4J if adp == :slf4j # okay, this is only because we can't camelize it :)
89
+
90
+ # Code adapter from ActiveSupport::Inflector#camelize
91
+ # https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L30
92
+ adp = adp.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } if adp.is_a?(Symbol)
93
+
94
+ clazz = adp
95
+
96
+ if adp.respond_to?(:to_str)
97
+ const = begin Loggr::Adapter.const_get(adp.to_s) rescue nil end
98
+ unless const
99
+ # code adapter from ActiveSupport::Inflector#constantize
100
+ # https://github.com/rails/rails/blob/v3.0.9/activesupport/lib/active_support/inflector/methods.rb#L107
101
+ names = adp.to_s.split('::')
102
+ names.shift if names.empty? || names.first.empty?
103
+
104
+ const = ::Object
105
+ names.each { |n| const = const.const_get(n) }
106
+ end
107
+ clazz = const
108
+ end
109
+
110
+ raise "#{clazz}: an adapter must implement #logger and #mdc" unless clazz.respond_to?(:logger) && clazz.respond_to?(:mdc)
111
+ clazz
112
+ end
113
+ end
114
+ end