polylog 0.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/History.txt +4 -0
- data/LICENSE.md +22 -0
- data/README.md +200 -0
- data/Rakefile +26 -0
- data/lib/polylog.rb +162 -0
- data/lib/polylog/errors.rb +15 -0
- data/lib/polylog/multi_provider.rb +80 -0
- data/lib/polylog/null_logger.rb +28 -0
- data/lib/polylog/solo_provider.rb +41 -0
- data/script/bootstrap +9 -0
- data/spec/polylog/multi_provider_spec.rb +41 -0
- data/spec/polylog/null_logger_spec.rb +82 -0
- data/spec/polylog/solo_provider_spec.rb +14 -0
- data/spec/polylog_spec.rb +124 -0
- data/spec/spec_helper.rb +31 -0
- data/version.txt +1 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30bf99c4368b2308581792e2f887e75e2533b1ef
|
4
|
+
data.tar.gz: 59f9b5c104970d4d73244db7381c9a197358271c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 29508ecd47dd8b423e799f8a7c71b8dc7bb8a8ed8de2120b15563f06e44bd126421ef342cedf615fc8f02967a839d9a13321d4641311f59823ca874d8bfda15d
|
7
|
+
data.tar.gz: d00c98550192a44ce783f61bd61c5c087a9c669e643921dee8efbe4a39c1bb6ac24d446049d1c63b3f1ffcd1337565f59133614b6c75d10dc698751efd3a1bae
|
data/.gitignore
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# The list of files that should be ignored by Mr Bones.
|
2
|
+
# Lines that start with '#' are comments.
|
3
|
+
#
|
4
|
+
# A .gitignore file can be used instead by setting it as the ignore
|
5
|
+
# file in your Rakefile:
|
6
|
+
#
|
7
|
+
# Bones {
|
8
|
+
# ignore_file '.gitignore'
|
9
|
+
# }
|
10
|
+
#
|
11
|
+
# For a project with a C extension, the following would be a good set of
|
12
|
+
# exclude patterns (uncomment them if you want to use them):
|
13
|
+
# *.[oa]
|
14
|
+
# *~
|
15
|
+
announcement.txt
|
16
|
+
coverage
|
17
|
+
doc
|
18
|
+
pkg
|
19
|
+
vendor
|
data/History.txt
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012-2013 by Tim Pease
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
## polylog
|
2
|
+
|
3
|
+
Decoupling the logging layer since 2012
|
4
|
+
|
5
|
+
### Description
|
6
|
+
|
7
|
+
Logging provides a simple mechanism for observing the state of a running
|
8
|
+
application, and most ruby gems and applications use some sort of logging.
|
9
|
+
This is great! Setting up a logging infrastructure across various gems and
|
10
|
+
applications is the not so great part.
|
11
|
+
|
12
|
+
In a Rails application it is assumed that all gems will use the `Rails.logger`
|
13
|
+
and that's the end of it. So as a gem developer you can just grab the Rails
|
14
|
+
logger and be done. The downside of that choice is your gem is now locked to
|
15
|
+
Rails and cannot be used elsewhere. Another option is to provide a
|
16
|
+
`setup( options = {} )` method (or something similar) and the application can
|
17
|
+
provide you with a logger. The downside of that choice is that applications
|
18
|
+
need to configure your gem and every other gem that adopts the same
|
19
|
+
convention.
|
20
|
+
|
21
|
+
A third option is to be clever and see if the Rails module exists when your
|
22
|
+
gem is loaded and then just grab the Rails logger. This makes testing
|
23
|
+
difficult and can lead to unintended side effects. Eschew clever code.
|
24
|
+
|
25
|
+
Polylog is an agreement between the gem developer and the application
|
26
|
+
developer on how logging is configured. For the gem developer, instead of
|
27
|
+
assuming the Rails environment or providing a `setup` method you ask Polylog
|
28
|
+
for a logger instance. For the application developer, instead of configuring
|
29
|
+
logging for multiple gems you configure Polylog with the logging
|
30
|
+
infrastructure you plan on using.
|
31
|
+
|
32
|
+
The gem developer no longer needs to worry what the application is going to
|
33
|
+
do. The logging for the gem does not need to be configured by the application.
|
34
|
+
Conversely, the application does not have to configure logging for every gem.
|
35
|
+
The application provides the logging infrastructure once to Polylog.
|
36
|
+
|
37
|
+
Let's see what this looks like in practice.
|
38
|
+
|
39
|
+
### Usage
|
40
|
+
|
41
|
+
The two primary use cases for Polylog are:
|
42
|
+
|
43
|
+
* obtaining a logger
|
44
|
+
* configuring the logging provider
|
45
|
+
|
46
|
+
Obtaining a logger is the domain of the gem developer (or supporting
|
47
|
+
application code). Configuring the logging provider is the domain of the
|
48
|
+
application.
|
49
|
+
|
50
|
+
#### Obtaining a Logger
|
51
|
+
|
52
|
+
Obtaining a logger is simple:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
Polylog.logger self
|
56
|
+
```
|
57
|
+
|
58
|
+
Passing `self` is optional but recommended. It provides context to Polylog and
|
59
|
+
allows object specific loggers to be returned (actually, they are class
|
60
|
+
specific). The application can configure different logger instances to be
|
61
|
+
returned depending upon the requesting class. This is useful if you want to
|
62
|
+
enable debugging for just one portion of the application.
|
63
|
+
|
64
|
+
In practice, you can create a module and include it where you want a `logger`
|
65
|
+
method to be available:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
module MyGem::Logger
|
69
|
+
def logger
|
70
|
+
Polylog.logger self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class MyGem::SomeClass
|
75
|
+
include MyGem::Logger
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
In this example each class in `MyGem` can obtain its own unique logger if
|
80
|
+
Polylog is configured to do so. But you can also develop your gem to use a
|
81
|
+
single logger instance, too.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
module MyGem::Logger
|
85
|
+
def logger
|
86
|
+
Polylog.logger 'MyGem'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Instead of passing `self` the top level namespace of the gem is used. Anywhere
|
92
|
+
this logger module is included the returned logger will be the same.
|
93
|
+
|
94
|
+
#### Configuring the Logging Provider
|
95
|
+
|
96
|
+
Without any configuration Polylog with provide a null logger for all requests.
|
97
|
+
This logger implements all the methods of the Ruby `Logger` class, but all the
|
98
|
+
methods do nothing. Not very useful except for preventing exceptions.
|
99
|
+
|
100
|
+
So lets log everything to standard out (a useful start):
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
provider = Polylog::SoloProvider.new(Logger.new(STDOUT))
|
104
|
+
Polylog.register_provider 'stdout', provider
|
105
|
+
|
106
|
+
Polylog.use_provider 'stdout'
|
107
|
+
```
|
108
|
+
|
109
|
+
Yikes! That looks pretty complicated. The three lines of code are the three
|
110
|
+
steps to configuring a logging provider.
|
111
|
+
|
112
|
+
The first line creates the provider we are going to use. The `SoloProvider`
|
113
|
+
provides exactly one logger. In the example it is providing a standard Ruby
|
114
|
+
`Logger` configured to log everything to `STDOUT`.
|
115
|
+
|
116
|
+
The second line registers the provider we created with Polylog. Multiple
|
117
|
+
providers can be registered but only one will be used. Each provider is
|
118
|
+
registered with a unique name. Logging frameworks like
|
119
|
+
[Log4r](https://github.com/colbygk/log4r), [Logging](https://github.com/twp/logging),
|
120
|
+
and [Scrolls](https://github.com/asenchi/scrolls) can register themselves with
|
121
|
+
Polylog. In this case, applications can skip the provider creation and just use
|
122
|
+
one that is already registered.
|
123
|
+
|
124
|
+
The third line tells Polylog to use the 'stdout' provider. All calls to
|
125
|
+
`Polylog.logger()` will lookup this provider and then call the `logger()`
|
126
|
+
method implemented by the provider. In this case, it is the solo-provider
|
127
|
+
which returns a STDOUT logger instance for all requests.
|
128
|
+
|
129
|
+
Lets register a few more providers:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
Polylog.register_provider 'stdout', Polylog::SoloProvider.new(Logger.new(STDOUT))
|
133
|
+
Polylog.register_provider 'stderr', Polylog::SoloProvider.new(Logger.new(STDERR))
|
134
|
+
Polylog.register_provider 'file', Polylog::SoloProvider.new(Logger.new('app.log'))
|
135
|
+
|
136
|
+
Polylog.use_provider 'file'
|
137
|
+
```
|
138
|
+
|
139
|
+
Even though we have providers for logging to `STDOUT` and `STDERR`, we have
|
140
|
+
chosen to send all our logs to a file instead. It is easy enough to swap
|
141
|
+
providers now. Because we are using the solo-provider all log messages will
|
142
|
+
end up going to the same destination.
|
143
|
+
|
144
|
+
We can send log messages to multiple destinations by using a multi-provider.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
provider = Polylog::MultiProvider.new(Logger.new(STDOUT))
|
148
|
+
provider['MyClass'] = Logger.new('my_class.log')
|
149
|
+
|
150
|
+
Polylog.register_provider 'my_class', provider
|
151
|
+
Polylog.use_provider 'my_class'
|
152
|
+
|
153
|
+
Polylog.logger # STDOUT logger
|
154
|
+
Polylog.logger(MyClass) # 'my_class.log' logger
|
155
|
+
```
|
156
|
+
|
157
|
+
The multi-provider can return more than one logger instance. It is configured
|
158
|
+
to return a default logger for all requests. However, a different logger can
|
159
|
+
be returned based on the Class of the object passed to the `Polylog.logger()`
|
160
|
+
method. In the example we are passing in `MyClass` to get the specific logger
|
161
|
+
for that class; instances of `MyClass` would also return the same logger.
|
162
|
+
|
163
|
+
If you want to get more creative with your logging configuration, I highly
|
164
|
+
recommend checking out the [Logging](https://github.com/twp/logging)
|
165
|
+
framework. It is designed to provide unique loggers and logging destinations.
|
166
|
+
|
167
|
+
#### Integrating With Rails
|
168
|
+
|
169
|
+
It would be wonderful if Polylog were integrated directly with Rails. This
|
170
|
+
would require Rails to adopt the `Polylog.logger()` style of acquiring a
|
171
|
+
logger instance. However, until that time happens, Polylog needs to be
|
172
|
+
configured with a provider that returns the `Rails.logger` instance.
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
Polylog.register_provider 'rails', Polylog::SoloProvider.new(Rails.logger)
|
176
|
+
Polylog.use_provider 'rails'
|
177
|
+
```
|
178
|
+
|
179
|
+
And that's pretty much it.
|
180
|
+
|
181
|
+
### Developing
|
182
|
+
|
183
|
+
Polylog makes use of Mr Bones to handle general project tasks such as running
|
184
|
+
tests, generating a gem file, interacting with GitHub, and publishing
|
185
|
+
to rubygems.org. A `script/bootstrap` script is provided to setup the
|
186
|
+
development environment:
|
187
|
+
|
188
|
+
```shell
|
189
|
+
script/bootstrap
|
190
|
+
```
|
191
|
+
|
192
|
+
After this is run, you can use `rake` normally to run tests. You can view all
|
193
|
+
the available rake tasks via `rake -T`.
|
194
|
+
|
195
|
+
```shell
|
196
|
+
rake -T
|
197
|
+
rake spec
|
198
|
+
```
|
199
|
+
|
200
|
+
Thank you for reading! Any contributions are greatly appreciated.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'bones'
|
4
|
+
rescue LoadError
|
5
|
+
abort '### Please install the "bones" gem ###'
|
6
|
+
end
|
7
|
+
|
8
|
+
task :default => 'spec:run'
|
9
|
+
task 'gem:release' => 'spec:run'
|
10
|
+
|
11
|
+
|
12
|
+
Bones {
|
13
|
+
name 'polylog'
|
14
|
+
authors 'Tim Pease'
|
15
|
+
email 'tim.pease@gmail.com'
|
16
|
+
url 'http://rubygems.org/gems/polylog'
|
17
|
+
|
18
|
+
spec.opts << '--color' << '--format documentation'
|
19
|
+
|
20
|
+
use_gmail
|
21
|
+
|
22
|
+
depend_on 'bones-rspec', :development => true
|
23
|
+
depend_on 'bones-git', :development => true
|
24
|
+
depend_on 'rdoc', :development => true
|
25
|
+
}
|
26
|
+
|
data/lib/polylog.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
|
2
|
+
module Polylog
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# Request a logger instance for the given object from the configured
|
6
|
+
# provider.
|
7
|
+
#
|
8
|
+
# object - Any ruby object
|
9
|
+
#
|
10
|
+
# Returns a logger instance from the provider.
|
11
|
+
def logger( object = nil )
|
12
|
+
name = logger_name object
|
13
|
+
provider.logger name
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return the currently selected provider. If a provider has not yet been
|
17
|
+
# specified, then the "null" provider will be used.
|
18
|
+
#
|
19
|
+
# Returns a logger provider.
|
20
|
+
def provider
|
21
|
+
use_provider('null') unless defined? @provider
|
22
|
+
@provider
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the list of available providers as an Array of Strings. These
|
26
|
+
# names are used to select which provider to use.
|
27
|
+
def providers
|
28
|
+
@providers.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
# Configure the logger provider that will be used by Polylog. The provider
|
32
|
+
# must already be registered using the same `name`.
|
33
|
+
#
|
34
|
+
# name - The provider name as a String.
|
35
|
+
#
|
36
|
+
# Returns the logger provider.
|
37
|
+
# Raises a Polylog::UnknownProvider exception.
|
38
|
+
def use_provider( name )
|
39
|
+
name = name.to_s
|
40
|
+
raise Polylog::UnknownProvider, "unknown provider: #{name.inspect}" unless @providers.key? name
|
41
|
+
|
42
|
+
@provider = @providers[name]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Register a logger provider under the given name. The provider can be any
|
46
|
+
# Ruby object that responds to the `logger` method and returns valid logger
|
47
|
+
# instances. An exception is raised if the provider does not respond to the
|
48
|
+
# `logger` method or the `logger` method does not have an arity of 1.
|
49
|
+
#
|
50
|
+
# name - The provider name as a String.
|
51
|
+
# provider - The provider object that responds to the `logger` method.
|
52
|
+
#
|
53
|
+
# Returns the logger provider.
|
54
|
+
# Raises a Polylog::InvalidProvider exception.
|
55
|
+
def register_provider( name, provider )
|
56
|
+
@providers ||= Hash.new
|
57
|
+
name = name.to_s
|
58
|
+
|
59
|
+
unless provider.respond_to?(:logger)
|
60
|
+
raise Polylog::InvalidProvider, "`logger` method not found for provider #{name.inspect}"
|
61
|
+
end
|
62
|
+
|
63
|
+
arity = provider.method(:logger).arity
|
64
|
+
unless 1 == arity
|
65
|
+
raise Polylog::InvalidProvider, "`logger` method arity must be 1; arity is #{arity} for provider #{name.inspect}"
|
66
|
+
end
|
67
|
+
|
68
|
+
@providers[name] = provider
|
69
|
+
end
|
70
|
+
|
71
|
+
# Internal: Given any ruby object, try to construct a sensible logger name
|
72
|
+
# to use for looking up specific logger instances from a provider. For
|
73
|
+
# nearly every object passed in this will be the class name of the instance.
|
74
|
+
#
|
75
|
+
# If you pass in `nil` or a String then the original object is return.
|
76
|
+
# Symbols will be converted to Strings.
|
77
|
+
#
|
78
|
+
# The only objects we cannot generate a logger name for are anonymous
|
79
|
+
# modules. For these we just return `nil`.
|
80
|
+
#
|
81
|
+
# object - Any ruby object
|
82
|
+
#
|
83
|
+
# Returns the logger name String or nil.
|
84
|
+
def logger_name( object )
|
85
|
+
case object
|
86
|
+
when nil, String; object
|
87
|
+
when Symbol; object.to_s
|
88
|
+
when Module; logger_name_for_module(object)
|
89
|
+
when Object; logger_name_for_module(object.class)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Internal: Generate logger names for classes, modules, singleton classes,
|
94
|
+
# and anonymous modules. For all of these we use the class name or the
|
95
|
+
# module name. For anonymous modules we return `nil`.
|
96
|
+
#
|
97
|
+
# mod - A Module or Class
|
98
|
+
#
|
99
|
+
# Returns the logger name String or nil.
|
100
|
+
def logger_name_for_module( mod )
|
101
|
+
return mod.name unless mod.name.nil? or mod.name.empty?
|
102
|
+
|
103
|
+
# check if we have a metaclass (or eigenclass)
|
104
|
+
if mod.ancestors.include? Class
|
105
|
+
mod.inspect =~ %r/#<Class:([^#>]+)>/
|
106
|
+
return $1
|
107
|
+
end
|
108
|
+
|
109
|
+
# see if we have a superclass
|
110
|
+
if mod.respond_to? :superclass
|
111
|
+
return logger_name_for_module(mod.superclass)
|
112
|
+
end
|
113
|
+
|
114
|
+
# we have an anonymous module
|
115
|
+
return nil
|
116
|
+
end
|
117
|
+
|
118
|
+
LIBPATH = ::File.expand_path('../', __FILE__)
|
119
|
+
PATH = ::File.dirname(LIBPATH)
|
120
|
+
|
121
|
+
# Returns the version String for the library.
|
122
|
+
def version
|
123
|
+
@version ||= File.read(path('version.txt')).strip
|
124
|
+
end
|
125
|
+
|
126
|
+
# Internal: Returns the library path for the module. If any arguments are
|
127
|
+
# given, they will be joined to the end of the library path using
|
128
|
+
# `File.join`.
|
129
|
+
def libpath( *args )
|
130
|
+
rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
131
|
+
if block_given?
|
132
|
+
begin
|
133
|
+
$LOAD_PATH.unshift LIBPATH
|
134
|
+
rv = yield
|
135
|
+
ensure
|
136
|
+
$LOAD_PATH.shift
|
137
|
+
end
|
138
|
+
end
|
139
|
+
return rv
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal: Returns the path for the module. If any arguments are given,
|
143
|
+
# they will be joined to the end of the path using `File.join`.
|
144
|
+
def path( *args )
|
145
|
+
rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
146
|
+
if block_given?
|
147
|
+
begin
|
148
|
+
$LOAD_PATH.unshift PATH
|
149
|
+
rv = yield
|
150
|
+
ensure
|
151
|
+
$LOAD_PATH.shift
|
152
|
+
end
|
153
|
+
end
|
154
|
+
return rv
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
require Polylog.libpath('polylog/errors')
|
160
|
+
require Polylog.libpath('polylog/solo_provider')
|
161
|
+
require Polylog.libpath('polylog/multi_provider')
|
162
|
+
require Polylog.libpath('polylog/null_logger')
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module Polylog
|
3
|
+
|
4
|
+
# Parent class for all Polylog errors.
|
5
|
+
Error = Class.new StandardError
|
6
|
+
|
7
|
+
# This is error is raised when an unknown provider is requested by the user.
|
8
|
+
UnknownProvider = Class.new Error
|
9
|
+
|
10
|
+
# This is error is raised when a provider is registered and it does not
|
11
|
+
# respond to the `logger` method or the `logger` method has an arity other
|
12
|
+
# than 1.
|
13
|
+
InvalidProvider = Class.new Error
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module Polylog
|
3
|
+
|
4
|
+
# The purpose of a MultiProvider is to provide a default logger instance
|
5
|
+
# when requested. Other loggers can be provided by registering them with the
|
6
|
+
# provider under the appropriate logger name. So when this name is passed to
|
7
|
+
# the `logger` method the registered logger instance will be returned.
|
8
|
+
#
|
9
|
+
# The example below shows how to use the MultiProvider to send all log
|
10
|
+
# messages from all instances of a single class to a log file instead of
|
11
|
+
# STDOUT. You might want to do this when debugging the class; you can set
|
12
|
+
# the logger level to debug and capture lots of text to a single location.
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# provider = Polylog::MultiProvider.new(Logger.new(STDOUT))
|
17
|
+
# provider['MyClass'] = Logger.new('my_class.log')
|
18
|
+
#
|
19
|
+
# Polylog.register_provider 'example', provider
|
20
|
+
# Polylog.use_provider 'example'
|
21
|
+
#
|
22
|
+
# logger = Polylog.logger # STDOUT logger
|
23
|
+
# my_class_logger = Polylog.logger(MyClass) # 'my_class.log' logger
|
24
|
+
#
|
25
|
+
class MultiProvider
|
26
|
+
|
27
|
+
# Create a new MultiProvider configured with the given logger as the
|
28
|
+
# default logger. Other loggers can be added to the provider. These will
|
29
|
+
# be returned by the provider (instead of the default logger) when their
|
30
|
+
# name is passed to the `logger` method.
|
31
|
+
#
|
32
|
+
# logger - The default logger to return.
|
33
|
+
def initialize( logger )
|
34
|
+
@hash = Hash.new
|
35
|
+
@default_logger = logger
|
36
|
+
end
|
37
|
+
|
38
|
+
# Accessor for getting and setting the default logger.
|
39
|
+
attr_accessor :default_logger
|
40
|
+
|
41
|
+
# Returns the named logger if it is present in the provider. If the named
|
42
|
+
# logger is not present then the default logger is returned.
|
43
|
+
#
|
44
|
+
# name - The logger name as a String.
|
45
|
+
def logger( name )
|
46
|
+
return default_logger if name.nil?
|
47
|
+
@hash.fetch(name, default_logger)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the logger or `nil` if the logger is not present.
|
51
|
+
#
|
52
|
+
# name - The logger name as a String.
|
53
|
+
def []( name )
|
54
|
+
@hash[name]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add a logger provider under the given name.
|
58
|
+
#
|
59
|
+
# name - The logger name as a String.
|
60
|
+
# logger - The logger.
|
61
|
+
#
|
62
|
+
# Returns the logger.
|
63
|
+
def []=( name, logger )
|
64
|
+
@hash[name] = logger
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns an Array with the names of the loggers present in the provider.
|
68
|
+
def loggers
|
69
|
+
@hash.keys
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns `true` if the logger is present in the provider.
|
73
|
+
#
|
74
|
+
# name - The logger name as a String.
|
75
|
+
def logger?( name )
|
76
|
+
@hash.key? name
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module Polylog
|
3
|
+
|
4
|
+
# This is a no-op logger that implements the principal methods from the
|
5
|
+
# standard Ruby logger. All methods do nothing and return either `false`
|
6
|
+
# or `nil`.
|
7
|
+
class NullLogger
|
8
|
+
def <<(msg) nil end
|
9
|
+
def close() nil end
|
10
|
+
|
11
|
+
def debug?() false end
|
12
|
+
alias :info? :debug?
|
13
|
+
alias :warn? :debug?
|
14
|
+
alias :error? :debug?
|
15
|
+
alias :fatal? :debug?
|
16
|
+
|
17
|
+
def add(*args) false end
|
18
|
+
alias :debug :add
|
19
|
+
alias :info :add
|
20
|
+
alias :warn :add
|
21
|
+
alias :error :add
|
22
|
+
alias :fatal :add
|
23
|
+
alias :unknown :add
|
24
|
+
alias :log :add
|
25
|
+
end
|
26
|
+
|
27
|
+
register_provider('null', SoloProvider.new(NullLogger.new))
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Polylog
|
2
|
+
|
3
|
+
# The purpose of the SoloProvider is to return a single instance for all
|
4
|
+
# logger requests. You would use this provider to supply all parts of the
|
5
|
+
# application with the exact same logger.
|
6
|
+
#
|
7
|
+
# The NullLogger uses the SoloProvider to register itself under the "null"
|
8
|
+
# provider name.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# Polylog.register_provider 'stdout', Polylog::SoloProvider.new(Logger.new(STDOUT))
|
13
|
+
# Polylog.register_provider 'stderr', Polylog::SoloProvider.new(Logger.new(STDERR))
|
14
|
+
# Polylog.register_provider 'file', Polylog::SoloProvider.new(Logger.new('app.log'))
|
15
|
+
#
|
16
|
+
# Polylog.use_provider 'stderr'
|
17
|
+
# logger = Polylog.logger # returns a STDERR logger
|
18
|
+
#
|
19
|
+
# Polylog.use_provider 'file'
|
20
|
+
# logger = Polylog.logger # returns an 'app.log' file logger
|
21
|
+
#
|
22
|
+
class SoloProvider
|
23
|
+
|
24
|
+
# Constructs a new SoloProvider that will provide the same instance for
|
25
|
+
# each request for a logger.
|
26
|
+
#
|
27
|
+
# logger - The logger instance that will be provided.
|
28
|
+
def initialize( logger )
|
29
|
+
@logger = logger
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the same logger instance for all requests. The `name` is not
|
33
|
+
# used by this this method.
|
34
|
+
#
|
35
|
+
# name - The logger name as a String
|
36
|
+
def logger( name )
|
37
|
+
@logger
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/script/bootstrap
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
3
|
+
|
4
|
+
describe Polylog::SoloProvider do
|
5
|
+
attr_reader :provider
|
6
|
+
|
7
|
+
context 'with only a default logger' do
|
8
|
+
before(:all) { @provider = Polylog::MultiProvider.new 'Multipass' }
|
9
|
+
|
10
|
+
it 'provides the same default logger' do
|
11
|
+
default = provider.default_logger
|
12
|
+
|
13
|
+
expect(provider.logger 'foo').to equal default
|
14
|
+
expect(provider.logger 'bar').to equal default
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when configured with multiple loggers' do
|
19
|
+
before(:all) do
|
20
|
+
@provider = Polylog::MultiProvider.new 'Multipass'
|
21
|
+
@provider['foo'] = 'FooLogger'
|
22
|
+
@provider['bar'] = 'BarLogger'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'provides loggers based on name' do
|
26
|
+
expect(provider.logger 'foo').to equal provider['foo']
|
27
|
+
expect(provider.logger 'bar').to equal provider['bar']
|
28
|
+
expect(provider.logger 'baz').to equal provider.default_logger
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns the list of available loggers' do
|
32
|
+
expect(provider.loggers).to match_array %w[foo bar]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'determines if a logger is present' do
|
36
|
+
expect(provider.logger? 'foo').to be_true
|
37
|
+
expect(provider.logger? 'bar').to be_true
|
38
|
+
expect(provider.logger? 'baz').to be_false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
3
|
+
|
4
|
+
describe Polylog::NullLogger do
|
5
|
+
attr_reader :null_logger
|
6
|
+
before(:all) { @null_logger = Polylog::NullLogger.new }
|
7
|
+
|
8
|
+
it 'responds to `<<`' do
|
9
|
+
expect(null_logger).to respond_to :<<
|
10
|
+
expect(null_logger << 'foo').to be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'responds to `close`' do
|
14
|
+
expect(null_logger).to respond_to :close
|
15
|
+
expect(null_logger.close).to be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'responds to `debug?`' do
|
19
|
+
expect(null_logger).to respond_to :debug?
|
20
|
+
expect(null_logger.debug?).to be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'responds to `info?`' do
|
24
|
+
expect(null_logger).to respond_to :info?
|
25
|
+
expect(null_logger.info?).to be_false
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'responds to `warn?`' do
|
29
|
+
expect(null_logger).to respond_to :warn?
|
30
|
+
expect(null_logger.warn?).to be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'responds to `error?`' do
|
34
|
+
expect(null_logger).to respond_to :error?
|
35
|
+
expect(null_logger.error?).to be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'responds to `fatal?`' do
|
39
|
+
expect(null_logger).to respond_to :fatal?
|
40
|
+
expect(null_logger.fatal?).to be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'responds to `add`' do
|
44
|
+
expect(null_logger).to respond_to :add
|
45
|
+
expect(null_logger.add 'foo').to be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'responds to `debug`' do
|
49
|
+
expect(null_logger).to respond_to :debug
|
50
|
+
expect(null_logger.debug 'foo').to be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'responds to `info`' do
|
54
|
+
expect(null_logger).to respond_to :info
|
55
|
+
expect(null_logger.info 'foo').to be_false
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'responds to `warn`' do
|
59
|
+
expect(null_logger).to respond_to :warn
|
60
|
+
expect(null_logger.warn 'foo').to be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'responds to `error`' do
|
64
|
+
expect(null_logger).to respond_to :error
|
65
|
+
expect(null_logger.error 'foo').to be_false
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'responds to `fatal`' do
|
69
|
+
expect(null_logger).to respond_to :fatal
|
70
|
+
expect(null_logger.fatal 'foo').to be_false
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'responds to `unknown`' do
|
74
|
+
expect(null_logger).to respond_to :unknown
|
75
|
+
expect(null_logger.unknown 'foo').to be_false
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'responds to `log`' do
|
79
|
+
expect(null_logger).to respond_to :log
|
80
|
+
expect(null_logger.log 'foo').to be_false
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
3
|
+
|
4
|
+
describe Polylog::SoloProvider do
|
5
|
+
attr_reader :provider
|
6
|
+
before(:all) { @provider = Polylog::SoloProvider.new 'Han Solo' }
|
7
|
+
|
8
|
+
it 'always provides the same instance' do
|
9
|
+
str = provider.logger nil
|
10
|
+
|
11
|
+
expect(provider.logger 'foo').to equal str
|
12
|
+
expect(provider.logger 'bar').to equal str
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../spec_helper', __FILE__)
|
3
|
+
|
4
|
+
describe Polylog do
|
5
|
+
|
6
|
+
it 'provides a canonical version string' do
|
7
|
+
expect(Polylog.version).to match %r/^\d+\.\d+\.\d+$/
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'without any configuration' do
|
11
|
+
it 'provides a null logger' do
|
12
|
+
expect(Polylog.logger).to be_an_instance_of Polylog::NullLogger
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'provides the smae null logger for all requests' do
|
16
|
+
null_logger = Polylog.logger
|
17
|
+
|
18
|
+
expect(Polylog.logger 'foo').to equal null_logger
|
19
|
+
expect(Polylog.logger 'bar').to equal null_logger
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'only has the null provider registered' do
|
23
|
+
expect(Polylog.providers).to eq %w[null]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when registering a provider' do
|
28
|
+
it 'raises an error for providers that have no `logger` method' do
|
29
|
+
expect {
|
30
|
+
Polylog.register_provider('nope', Object.new)
|
31
|
+
}.to raise_error(Polylog::InvalidProvider, /method not found for provider/)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'raises an error when the `logger` method has the wrong airty' do
|
35
|
+
provider = Object.new
|
36
|
+
class << provider
|
37
|
+
def logger() nil; end
|
38
|
+
end
|
39
|
+
|
40
|
+
expect {
|
41
|
+
Polylog.register_provider('nope', provider)
|
42
|
+
}.to raise_error(Polylog::InvalidProvider, /method arity must be 1/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'adds the provider to the registry' do
|
46
|
+
expect(Polylog.providers).not_to include('test1')
|
47
|
+
|
48
|
+
provider = Polylog::SoloProvider.new(:test1)
|
49
|
+
Polylog.register_provider 'test1', provider
|
50
|
+
|
51
|
+
expect(Polylog.providers).to include('test1')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'overwrites existing providers' do
|
55
|
+
provider = Polylog::SoloProvider.new(:test2)
|
56
|
+
Polylog.register_provider 'test2', provider
|
57
|
+
expect(Polylog.providers).to include('test2')
|
58
|
+
Polylog.use_provider 'test2'
|
59
|
+
|
60
|
+
provider = Polylog::SoloProvider.new(:overwrite)
|
61
|
+
Polylog.register_provider 'test2', provider
|
62
|
+
|
63
|
+
expect(Polylog.logger).to equal :test2
|
64
|
+
Polylog.use_provider 'test2'
|
65
|
+
expect(Polylog.logger).to equal :overwrite
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when selecting a provider to use' do
|
70
|
+
it 'raises an error for unknown providers' do
|
71
|
+
expect {
|
72
|
+
Polylog.use_provider 'foo'
|
73
|
+
}.to raise_error(Polylog::UnknownProvider, 'unknown provider: "foo"')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'can use a registered provider' do
|
77
|
+
provider = Polylog::SoloProvider.new(:test3)
|
78
|
+
Polylog.register_provider 'test3', provider
|
79
|
+
|
80
|
+
expect(Polylog.providers).to include('test3')
|
81
|
+
|
82
|
+
Polylog.use_provider 'test3'
|
83
|
+
expect(Polylog.logger).to equal :test3
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when generating logger names' do
|
88
|
+
it 'uses nil as the default' do
|
89
|
+
expect(Polylog.logger_name nil).to be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'uses String names as is' do
|
93
|
+
expect(Polylog.logger_name 'foo').to eql 'foo'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'converts Symbols to Strings' do
|
97
|
+
expect(Polylog.logger_name :foo).to eql 'foo'
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'uses Module names' do
|
101
|
+
expect(Polylog.logger_name Enumerable).to eql 'Enumerable'
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'uses Class names for instances' do
|
105
|
+
expect(Polylog.logger_name Object).to eql 'Object'
|
106
|
+
expect(Polylog.logger_name Object.new).to eql 'Object'
|
107
|
+
expect(Polylog.logger_name []).to eql 'Array'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'uses the orignal Class name for singletons (eigenclasses)' do
|
111
|
+
ary = []
|
112
|
+
eigen = class << ary; self; end
|
113
|
+
|
114
|
+
expect(Polylog.logger_name eigen).to eql 'Array'
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'gives up when confronted with an anonymous module' do
|
118
|
+
m = Module.new { def foo() nil end }
|
119
|
+
expect(Polylog.logger_name m).to be_nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require File.expand_path('../../lib/polylog', __FILE__)
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.before(:all) { Polylog.reset! }
|
6
|
+
|
7
|
+
# == Mock Framework
|
8
|
+
#
|
9
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
10
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
# config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
end
|
16
|
+
|
17
|
+
module Polylog
|
18
|
+
# Only used for testing. This resets the Polylog providers configuration to
|
19
|
+
# its uninitialized state.
|
20
|
+
def reset!
|
21
|
+
remove_instance_variable :@provider if defined? @provider
|
22
|
+
|
23
|
+
@providers.keys.each do |key|
|
24
|
+
next if key == 'null'
|
25
|
+
@providers.delete key
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/version.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: polylog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Pease
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bones-rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.1
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bones-git
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.3.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rdoc
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.12.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.12.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bones
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.8.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.8.1
|
69
|
+
description: ''
|
70
|
+
email: tim.pease@gmail.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files:
|
74
|
+
- History.txt
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- History.txt
|
78
|
+
- LICENSE.md
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/polylog.rb
|
82
|
+
- lib/polylog/errors.rb
|
83
|
+
- lib/polylog/multi_provider.rb
|
84
|
+
- lib/polylog/null_logger.rb
|
85
|
+
- lib/polylog/solo_provider.rb
|
86
|
+
- script/bootstrap
|
87
|
+
- spec/polylog/multi_provider_spec.rb
|
88
|
+
- spec/polylog/null_logger_spec.rb
|
89
|
+
- spec/polylog/solo_provider_spec.rb
|
90
|
+
- spec/polylog_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- version.txt
|
93
|
+
homepage: http://rubygems.org/gems/polylog
|
94
|
+
licenses: []
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options:
|
98
|
+
- --main
|
99
|
+
- README.md
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project: polylog
|
114
|
+
rubygems_version: 2.0.3
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: ''
|
118
|
+
test_files: []
|