log_mixin 1.1.2
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/README.md +92 -0
- data/lib/log_mixin.rb +263 -0
- data/log_mixin.gemspec +36 -0
- metadata +92 -0
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
LogMixin
|
2
|
+
===
|
3
|
+
|
4
|
+
The LogMixin module provides (you guessed it) a mixin to make logging more
|
5
|
+
convenient. It is intended to work both with and without Rails, to silence
|
6
|
+
logging in tests by default but provide access to it when explicitly
|
7
|
+
requested, to log to stderr (or Rails.logger) by default but accept other
|
8
|
+
logging targets, and other conveniences.
|
9
|
+
|
10
|
+
Released under the three-clause BSD open source license.
|
11
|
+
http://opensource.org/licenses/BSD-3-Clause
|
12
|
+
|
13
|
+
Usage
|
14
|
+
===
|
15
|
+
```ruby
|
16
|
+
class Foo
|
17
|
+
include LogMixin
|
18
|
+
|
19
|
+
def do_something_harmless
|
20
|
+
self.debug("Arcane internal debugging parameters: foo=#{foo} bar=#{bar}")
|
21
|
+
self.info("Doing something harmless...")
|
22
|
+
# ...
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_something_risky
|
26
|
+
if self.on_fire?
|
27
|
+
self.err("Ignition initiated")
|
28
|
+
self.fatal("HELP! I'm on fire!") if on_fire
|
29
|
+
else
|
30
|
+
self.warn("Temperature is rising, but not on fire yet")
|
31
|
+
end
|
32
|
+
# ...
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Logging and Tests
|
38
|
+
===
|
39
|
+
By default, log messages are silenced during tests. This is usually what
|
40
|
+
you want.
|
41
|
+
|
42
|
+
Sometimes you may want to test logging behavior. It's usually not advisable
|
43
|
+
to test the *content* of log messages. If you know what you're signing up
|
44
|
+
for, you might test that an INFO message was logged rather than a WARNING,
|
45
|
+
however. Be careful -- you're creating an implicit contract. That having
|
46
|
+
been said, here is how you can test log messages in RSpec:
|
47
|
+
```ruby
|
48
|
+
it 'should log correctly' do
|
49
|
+
obj = MyClass.new
|
50
|
+
obj.__handle.msgs.should have(0).messages
|
51
|
+
obj.do_something_that_logs_twice
|
52
|
+
obj.__handle.msgs.should have(2).messages
|
53
|
+
obj.do_something_that_logs_an_error
|
54
|
+
obj.__handle.msgs.select {|msg| msg =~ /ERROR/}.should have(1).message
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
What if you're running your tests and you have a bug, but you can't figure
|
59
|
+
out what's going on, so you actually _want_ to see the log messages? Well,
|
60
|
+
you can. Here is how you activate logging during tests, which you should
|
61
|
+
presumably only do temporarily:
|
62
|
+
```ruby
|
63
|
+
['TESTING', 'RAILS'].each do |c|
|
64
|
+
LogMixin.send(:remove_const, c)
|
65
|
+
LogMixin.const_set(c, false)
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
LogMixin supports both inclusion (i.e., instances of the class get the
|
70
|
+
logging methods) and extension (i.e., the class or module itself get the
|
71
|
+
logging methods).
|
72
|
+
```ruby
|
73
|
+
class ChattyInstance
|
74
|
+
include LogMixin
|
75
|
+
...
|
76
|
+
end
|
77
|
+
|
78
|
+
module ChattyModule
|
79
|
+
extend LogMixin
|
80
|
+
...
|
81
|
+
end
|
82
|
+
|
83
|
+
c = ChattyInstance.new
|
84
|
+
c.info("I'm an instance!")
|
85
|
+
ChattyModule.info("I'm a module!")
|
86
|
+
LogMixin.info("You can even log with LogMixin itself...")
|
87
|
+
LogMixin.warn("...but 'mixin' is a bit of a misnomer in that case.")
|
88
|
+
```
|
89
|
+
|
90
|
+
Want to change the log message format? You have lots of flexibility. See
|
91
|
+
the documentation for ```#configure_logs``` and ```VBLM_DEFAULT_FORMAT```.
|
92
|
+
|
data/lib/log_mixin.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
# Mix-in class to support logging under Heroku, local Rails, command line,
|
2
|
+
# test environment, etc. Especially useful for utilities which might not
|
3
|
+
# be run under Rails, so Rails.logger isn't an option.
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# class MyClass
|
7
|
+
# include LogMixin
|
8
|
+
# # if you write an initializer, invoke configure_logs there -- see below
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# obj = MyClass.new
|
13
|
+
# obj.info("Something happened!") # default severity is INFO
|
14
|
+
# obj.fatal("EMERGENCY!")
|
15
|
+
#
|
16
|
+
# Testing the contents of log messages is suspicious, but you may want to
|
17
|
+
# test that logging occurred, and in any case, we won't stop you from testing
|
18
|
+
# what you want. Here's how you access the logging from a test:
|
19
|
+
#
|
20
|
+
# it 'should log correctly' do
|
21
|
+
# obj = MyClass.new
|
22
|
+
# obj.__handle.msgs.should have(0).messages
|
23
|
+
# obj.do_something_that_logs_twice
|
24
|
+
# obj.__handle.msgs.should have(2).messages
|
25
|
+
# obj.do_something_that_logs_an_error
|
26
|
+
# obj.__handle.msgs.select {|msg| msg =~ /ERROR/}.should have(1).message
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Want to hack your tests so that you temporarily output log messages?
|
30
|
+
# ['TESTING', 'RAILS'].each do |c|
|
31
|
+
# LogMixin.send(:remove_const, c)
|
32
|
+
# LogMixin.const_set(c, false)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
|
36
|
+
require 'time' # for time-stamping log messages
|
37
|
+
|
38
|
+
module LogMixin
|
39
|
+
|
40
|
+
class Error < StandardError; end
|
41
|
+
class InitError < Error; end
|
42
|
+
|
43
|
+
# If your class has its own initializer, you probably want to invoke
|
44
|
+
# configure_logs in that initializer. If you don't write an initializer
|
45
|
+
# and use the default, this should suffice.
|
46
|
+
def initialize(*args)
|
47
|
+
super(*args)
|
48
|
+
configure_logs # with no other info, configure with default params
|
49
|
+
end
|
50
|
+
|
51
|
+
############################## FOR TEST ONLY ##############################
|
52
|
+
|
53
|
+
# Special-case the condition of running under a test environment
|
54
|
+
# We probably don't want to spew logging output when running unit tests
|
55
|
+
TESTING = Object.constants.include?(:RSpec)
|
56
|
+
|
57
|
+
# FakeFileHandle is a place to send test logging output
|
58
|
+
class FakeFileHandle
|
59
|
+
attr_reader :msgs
|
60
|
+
def initialize; @msgs = []; end
|
61
|
+
def print(msg); @msgs << msg; end
|
62
|
+
end
|
63
|
+
|
64
|
+
############################## END TEST ONLY ##############################
|
65
|
+
|
66
|
+
# If we're running under Rails, then we'll probably want to use the Rails
|
67
|
+
# logging facilities.
|
68
|
+
RAILS = Object.constants.include? :Rails
|
69
|
+
|
70
|
+
# Log levels: 0 = CRITICAL, 1 = ERROR, 2 = WARNING, 3 = INFO, 4 = DEBUG
|
71
|
+
# Any other integer is also valid, but doesn't map to a named level.
|
72
|
+
# An *object's* log level is its threshold for caring about log messages.
|
73
|
+
# A *message's* log level is its importance.
|
74
|
+
#
|
75
|
+
|
76
|
+
VBLM_LOG_LEVEL_NAMES = {
|
77
|
+
:critical => 0,
|
78
|
+
:error => 1,
|
79
|
+
:warning => 2,
|
80
|
+
:info => 3,
|
81
|
+
:debug => 4,
|
82
|
+
}
|
83
|
+
VBLM_DEFAULT_LOG_LEVEL = :info
|
84
|
+
|
85
|
+
# Default format looks like this:
|
86
|
+
# "[2012-04-18 12:00:00] MyObject: ERROR: Something went wrong"
|
87
|
+
# Note timestamp, caller, severity, message
|
88
|
+
#
|
89
|
+
# When creating custom formats, :caller can be either a fixed string (such as
|
90
|
+
# the program name or '') or a function on the calling object (as below).
|
91
|
+
# :severity can be a fixed string (usually '') or a function on the
|
92
|
+
# integer log_level.
|
93
|
+
# :timestamp must be a string, and may include any format chars understood by
|
94
|
+
# Time.strftime
|
95
|
+
VBLM_DEFAULT_FORMAT = {
|
96
|
+
:timestamp => '[%Y-%m-%d %H:%M:%S] ',
|
97
|
+
:caller => lambda do |ob|
|
98
|
+
%{Module Class}.include?(ob.class.name) ?
|
99
|
+
"#{ob.name}: " : "#{ob.class.name}: "
|
100
|
+
end,
|
101
|
+
:severity => lambda { |level|
|
102
|
+
level = self.log_level_int(level)
|
103
|
+
((level >= 0 and level <= 4) ?
|
104
|
+
%W{CRITICAL ERROR WARNING INFO DEBUG}[level] :
|
105
|
+
"Log level #{level.to_s}") + ": " },
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
# We want to handle "log_level = 3" and "log_level = :info" with equal grace.
|
110
|
+
# This function canonicalizes its input to an integer (e.g. :info becomes 3)
|
111
|
+
def self.log_level_int(symbol_or_int)
|
112
|
+
if symbol_or_int.class == Symbol && VBLM_LOG_LEVEL_NAMES.key?(symbol_or_int)
|
113
|
+
return VBLM_LOG_LEVEL_NAMES[symbol_or_int]
|
114
|
+
end
|
115
|
+
if symbol_or_int.class == Fixnum
|
116
|
+
return symbol_or_int
|
117
|
+
end
|
118
|
+
raise ArgumentError, "Invalid log level: #{symbol_or_int.to_s}"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Any object using the LogMixin module must call configure_logs() before
|
122
|
+
# starting to log.
|
123
|
+
# * :log_handles is a list of objects which respond to "print"
|
124
|
+
# * :log_level is the maximum (lowest-priority) level that this object cares
|
125
|
+
# about logging -- log messages of higher log_level (lower priority)
|
126
|
+
# will be ignored
|
127
|
+
# * :format is a hash with any or all of [:timestamp, :caller, :severity]
|
128
|
+
# * keys which are nil or not overridden will retain default values
|
129
|
+
# * for details of hash values, see VBLM_DEFAULT_FORMAT above
|
130
|
+
#
|
131
|
+
def configure_logs(options={})
|
132
|
+
default_options = {
|
133
|
+
:log_handles => [$stdout],
|
134
|
+
:log_level => VBLM_DEFAULT_LOG_LEVEL,
|
135
|
+
:format => VBLM_DEFAULT_FORMAT,
|
136
|
+
:test_logging_internals => false,
|
137
|
+
}
|
138
|
+
opts = default_options
|
139
|
+
opts.update(options)
|
140
|
+
# Are we testing? If so, don't output logs, just keep them in memory.
|
141
|
+
# (Unless we're testing the internals of the logging module.)
|
142
|
+
if TESTING and not opts[:test_logging_internals] then
|
143
|
+
@__handle = FakeFileHandle.new
|
144
|
+
@vblm_log_handles = [@__handle]
|
145
|
+
else
|
146
|
+
@vblm_log_handles = opts[:log_handles] || []
|
147
|
+
end
|
148
|
+
@vblm_log_level = opts[:log_level]
|
149
|
+
@vblm_format = opts[:format] || VBLM_DEFAULT_FORMAT
|
150
|
+
# Caller can request e.g. custom timestamp, keeping other default formatting
|
151
|
+
[:timestamp, :caller, :severity].each do |key|
|
152
|
+
@vblm_format[key] ||= VBLM_DEFAULT_FORMAT[key]
|
153
|
+
end
|
154
|
+
@__log_mixin_initialized = true
|
155
|
+
end
|
156
|
+
|
157
|
+
# For changing log level of a running process, e.g. via HTTP
|
158
|
+
def setLogLevel(log_level)
|
159
|
+
@vblm_log_level = log_level
|
160
|
+
end
|
161
|
+
|
162
|
+
# Handle parameters (formatting options, in our case) which may either be
|
163
|
+
# fixed strings or functions returning strings. If the input is a function,
|
164
|
+
# call it with the given *args and return its result.
|
165
|
+
#
|
166
|
+
def stringOrFunctionOutput(s_or_f, *args)
|
167
|
+
s_or_f.respond_to?('call') ? s_or_f.call(*args) : s_or_f.to_s
|
168
|
+
end
|
169
|
+
|
170
|
+
# Return the message formatted according to this LogMixin object's params
|
171
|
+
# (Usually this means prepending a timestamp, caller, and severity, and
|
172
|
+
# adding a newline.)
|
173
|
+
#
|
174
|
+
def formattedMessage(msg, options={})
|
175
|
+
default_options = { :log_level => VBLM_DEFAULT_LOG_LEVEL }
|
176
|
+
opts = default_options
|
177
|
+
opts.update(options)
|
178
|
+
timestamp = Time.now.strftime(@vblm_format[:timestamp])
|
179
|
+
# :caller and :severity may be fixed strings, or functions returning strings
|
180
|
+
caller = stringOrFunctionOutput(@vblm_format[:caller], self)
|
181
|
+
severity = stringOrFunctionOutput(@vblm_format[:severity], opts[:log_level])
|
182
|
+
"#{timestamp}#{caller}#{severity}#{msg}\n"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Use Rails.logger to do the logging
|
186
|
+
# This assumes that the caller has detected a Rails environment.
|
187
|
+
#
|
188
|
+
def __railsLog(msg, level)
|
189
|
+
# First, squash log levels worse than CRITICAL to CRITICAL, and levels
|
190
|
+
# more benign than DEBUG to DEBUG.
|
191
|
+
rails_level = level
|
192
|
+
if rails_level < LogMixin::log_level_int(:critical) then
|
193
|
+
rails_level = LogMixin::log_level_int(:critical)
|
194
|
+
elsif rails_level > LogMixin::log_level_int(:debug) then
|
195
|
+
rails_level = LogMixin::log_level_int(:debug)
|
196
|
+
end
|
197
|
+
case rails_level
|
198
|
+
when LogMixin::log_level_int(:debug) then Rails.logger.debug(msg)
|
199
|
+
when LogMixin::log_level_int(:info) then Rails.logger.info(msg)
|
200
|
+
when LogMixin::log_level_int(:warning) then Rails.logger.warn(msg)
|
201
|
+
when LogMixin::log_level_int(:error) then Rails.logger.error(msg)
|
202
|
+
when LogMixin::log_level_int(:critical) then Rails.logger.fatal(msg)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
# Print the message to "all" logging handles, if the message level is of
|
206
|
+
# sufficient priority that this object cares about it (i.e. if the numeric
|
207
|
+
# message log level is <= the object log level). Silently discard the
|
208
|
+
# message if its priority is insufficient.
|
209
|
+
#
|
210
|
+
# "All" filehandles include everything in @vblm_log_handles, except that
|
211
|
+
# if we are running under Rails, the default handle $stdout is ignored
|
212
|
+
# and replaced with the Rails logger.
|
213
|
+
#
|
214
|
+
def log(msg, options={})
|
215
|
+
if not @__log_mixin_initialized
|
216
|
+
raise InitError, "#{self.class} object uses LogMixin " +
|
217
|
+
"but never called configure_logs"
|
218
|
+
end
|
219
|
+
default_options = {:level => VBLM_DEFAULT_LOG_LEVEL}
|
220
|
+
opts = default_options
|
221
|
+
opts.update(options)
|
222
|
+
|
223
|
+
level = LogMixin::log_level_int(opts[:level])
|
224
|
+
return if level > LogMixin::log_level_int(@vblm_log_level)
|
225
|
+
|
226
|
+
# Write the log message, in the appropriate style
|
227
|
+
formatted_msg = self.formattedMessage(msg, :log_level => level)
|
228
|
+
if RAILS then
|
229
|
+
# log Rails-style, with Rails.logger.{debug,info,warn,error,fatal}
|
230
|
+
__railsLog(msg, level)
|
231
|
+
else
|
232
|
+
# format message, and print to $stdout (unless $stdout isn't requested)
|
233
|
+
$stdout.print(formatted_msg) if @vblm_log_handles.include?($stdout)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Print to the provided file handles (except stdout, which we've covered)
|
237
|
+
@vblm_log_handles.reject {|h| h == $stdout}.each do |handle|
|
238
|
+
# Support lazy-evaluation of log handles, so that loggable objects can
|
239
|
+
# be instantiated without creating log files until they actually
|
240
|
+
# log something.
|
241
|
+
if handle.respond_to?(:call) then
|
242
|
+
@vblm_log_handles[@vblm_log_handles.index(handle)] = handle.call
|
243
|
+
handle = handle.call
|
244
|
+
end
|
245
|
+
|
246
|
+
handle.print(formatted_msg)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def debug(msg, options={}); log(msg, options.merge(level: :debug )); end
|
251
|
+
def info(msg, options={}); log(msg, options.merge(level: :info )); end
|
252
|
+
def warn(msg, options={}); log(msg, options.merge(level: :warning )); end
|
253
|
+
def err(msg, options={}); log(msg, options.merge(level: :error )); end
|
254
|
+
def fatal(msg, options={}); log(msg, options.merge(level: :critical)); end
|
255
|
+
|
256
|
+
extend self # can be invoked as module methods or object methods
|
257
|
+
configure_logs
|
258
|
+
|
259
|
+
# The configure_logs call above doesn't apply when some other module extends
|
260
|
+
# LogMixin, so the following call addresses that.
|
261
|
+
def self.extended(base); base.configure_logs; end
|
262
|
+
|
263
|
+
end # module LogMixin
|
data/log_mixin.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = %q{log_mixin}
|
4
|
+
s.version = '1.1.2'
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.required_ruby_version = '>= 1.9.3'
|
7
|
+
|
8
|
+
if s.respond_to? :required_rubygems_version=
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2")
|
10
|
+
end
|
11
|
+
s.authors = ['Jon Snitow']
|
12
|
+
s.email = ['opensource@verticalbrands.com']
|
13
|
+
s.date = '2012-10-19'
|
14
|
+
s.summary = 'Mixin module for easy logging under various circumstances'
|
15
|
+
s.description = <<-EOT
|
16
|
+
The LogMixin module provides (you guessed it) a mixin to make logging more
|
17
|
+
convenient. It is intended to work both with and without Rails, to silence
|
18
|
+
logging in tests by default but provide access to it when explicitly
|
19
|
+
requested, to log to stderr (or Rails.logger) by default but accept other
|
20
|
+
logging targets, and other conveniences.
|
21
|
+
EOT
|
22
|
+
s.files = Dir[
|
23
|
+
'{lib}/**/*.rb',
|
24
|
+
'LICENSE',
|
25
|
+
'*.md',
|
26
|
+
'log_mixin.gemspec',
|
27
|
+
]
|
28
|
+
s.require_paths = ['lib']
|
29
|
+
|
30
|
+
s.rubyforge_project = 'log_mixin'
|
31
|
+
s.rubygems_version = '>= 1.8.6'
|
32
|
+
s.homepage = 'http://github.com/apartmentlist'
|
33
|
+
|
34
|
+
s.add_development_dependency('rspec')
|
35
|
+
s.add_development_dependency('rr')
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: log_mixin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jon Snitow
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rr
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: ! 'The LogMixin module provides (you guessed it) a mixin to make logging
|
47
|
+
more
|
48
|
+
|
49
|
+
convenient. It is intended to work both with and without Rails, to silence
|
50
|
+
|
51
|
+
logging in tests by default but provide access to it when explicitly
|
52
|
+
|
53
|
+
requested, to log to stderr (or Rails.logger) by default but accept other
|
54
|
+
|
55
|
+
logging targets, and other conveniences.
|
56
|
+
|
57
|
+
'
|
58
|
+
email:
|
59
|
+
- opensource@verticalbrands.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- lib/log_mixin.rb
|
65
|
+
- README.md
|
66
|
+
- log_mixin.gemspec
|
67
|
+
homepage: http://github.com/apartmentlist
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 1.9.3
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '1.2'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project: log_mixin
|
87
|
+
rubygems_version: 1.8.19
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Mixin module for easy logging under various circumstances
|
91
|
+
test_files: []
|
92
|
+
has_rdoc:
|