logging 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +11 -0
- data/lib/logging.rb +129 -0
- data/lib/logging/appender.rb +205 -0
- data/lib/logging/appenders/console.rb +47 -0
- data/lib/logging/appenders/file.rb +45 -0
- data/lib/logging/appenders/io.rb +87 -0
- data/lib/logging/appenders/static_appender.rb +58 -0
- data/lib/logging/layout.rb +102 -0
- data/lib/logging/layouts/basic.rb +48 -0
- data/lib/logging/layouts/pattern.rb +320 -0
- data/lib/logging/log_event.rb +52 -0
- data/lib/logging/logger.rb +345 -0
- data/lib/logging/repository.rb +148 -0
- data/lib/logging/root_logger.rb +69 -0
- data/test/appenders/test_console.rb +40 -0
- data/test/appenders/test_file.rb +76 -0
- data/test/appenders/test_io.rb +113 -0
- data/test/benchmark.rb +80 -0
- data/test/layouts/test_basic.rb +46 -0
- data/test/layouts/test_pattern.rb +175 -0
- data/test/setup.rb +45 -0
- data/test/test_all.rb +5 -0
- data/test/test_appender.rb +136 -0
- data/test/test_layout.rb +85 -0
- data/test/test_log_event.rb +81 -0
- data/test/test_logger.rb +472 -0
- data/test/test_logging.rb +121 -0
- data/test/test_repository.rb +113 -0
- data/test/test_root_logger.rb +67 -0
- metadata +75 -0
data/README.txt
ADDED
data/lib/logging.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# $Id: logging.rb 12 2007-01-14 20:03:40Z tim_pease $
|
2
|
+
|
3
|
+
require 'logging/repository'
|
4
|
+
|
5
|
+
# require all appenders
|
6
|
+
require 'logging/appenders/console'
|
7
|
+
require 'logging/appenders/file'
|
8
|
+
require 'logging/appenders/static_appender'
|
9
|
+
|
10
|
+
# require all layouts
|
11
|
+
require 'logging/layouts/basic'
|
12
|
+
require 'logging/layouts/pattern'
|
13
|
+
|
14
|
+
|
15
|
+
#
|
16
|
+
#
|
17
|
+
#
|
18
|
+
module Logging
|
19
|
+
|
20
|
+
LEVELS = {} # :nodoc:
|
21
|
+
LNAMES = {} # :nodoc:
|
22
|
+
|
23
|
+
class << self
|
24
|
+
#
|
25
|
+
# call-seq:
|
26
|
+
# define_levels( levels )
|
27
|
+
#
|
28
|
+
# Defines the levels available to the loggers. The _levels_ is an array
|
29
|
+
# of strings and symbols. Each element in the array is downcased and
|
30
|
+
# converted to a symbol; these symbols are used to create the logging
|
31
|
+
# methods in the loggers.
|
32
|
+
#
|
33
|
+
# The first element in the array is the lowest logging level. Setting the
|
34
|
+
# logging level to this value will enable all log messages. The last
|
35
|
+
# element in the array is the highest logging level. Setting the logging
|
36
|
+
# level to this value will disable all log messages except this highest
|
37
|
+
# level.
|
38
|
+
#
|
39
|
+
# This method should only be invoked once to configure the logging
|
40
|
+
# levels. It is automatically invoked with the default logging levels
|
41
|
+
# when the first logger is created.
|
42
|
+
#
|
43
|
+
# The levels "all" and "off" are reserved and will be ignored if passed
|
44
|
+
# to this method.
|
45
|
+
#
|
46
|
+
# Example:
|
47
|
+
#
|
48
|
+
# Logging.define_levels :debug, :info, :warn, :error, :fatal
|
49
|
+
# log = Logging::Logger['my logger']
|
50
|
+
# log.level = :warn
|
51
|
+
# log.warn 'Danger! Danger! Will Robinson'
|
52
|
+
# log.info 'Just FYI' # => not logged
|
53
|
+
#
|
54
|
+
# or
|
55
|
+
#
|
56
|
+
# Logging.define_levels %w(DEBUG INFO NOTICE WARNING ERR CRIT ALERT EMERG)
|
57
|
+
# log = Logging::Logger['syslog']
|
58
|
+
# log.level = :notice
|
59
|
+
# log.warning 'This is your first warning'
|
60
|
+
# log.info 'Just FYI' # => not logged
|
61
|
+
#
|
62
|
+
def define_levels( *args )
|
63
|
+
return nil if args.empty?
|
64
|
+
|
65
|
+
args.flatten!
|
66
|
+
levels = ::Logging::LEVELS.clear
|
67
|
+
names = ::Logging::LNAMES.clear
|
68
|
+
|
69
|
+
id = 0
|
70
|
+
args.each do |lvl|
|
71
|
+
lvl = levelify lvl
|
72
|
+
unless levels.has_key?(lvl) or lvl == 'all' or lvl == 'off'
|
73
|
+
levels[lvl] = id
|
74
|
+
names[id] = lvl.upcase
|
75
|
+
id += 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
longest = names.values.inject {|x,y| (x.length > y.length) ? x : y}
|
80
|
+
module_eval "MAX_LEVEL_LENGTH = #{longest.length}"
|
81
|
+
|
82
|
+
levels.keys
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# call-seq:
|
87
|
+
# format_as( obj_format )
|
88
|
+
#
|
89
|
+
# Defines the default _obj_format_ method to use when converting objects
|
90
|
+
# into string representations for logging. _obj_format_ can be one of
|
91
|
+
# <tt>:string</tt>, <tt>:inspect</tt>, or <tt>:yaml</tt>. These
|
92
|
+
# formatting commands map to the following object methods
|
93
|
+
#
|
94
|
+
# * :string => to_s
|
95
|
+
# * :inspect => inspect
|
96
|
+
# * :yaml => to_yaml
|
97
|
+
#
|
98
|
+
# An +ArgumentError+ is raised if anything other than +:string+,
|
99
|
+
# +:inspect+, +:yaml+ is passed to this method.
|
100
|
+
#
|
101
|
+
def format_as( f )
|
102
|
+
unless [:string, :inspect, :yaml].include? f
|
103
|
+
raise ArgumentError, "unknown object format '#{f}'"
|
104
|
+
end
|
105
|
+
|
106
|
+
module_eval "OBJ_FORMAT = :#{f}"
|
107
|
+
end
|
108
|
+
|
109
|
+
# :stopdoc:
|
110
|
+
def levelify( level )
|
111
|
+
case level
|
112
|
+
when String: level.downcase
|
113
|
+
when Symbol: level.to_s.downcase
|
114
|
+
else raise ArgumentError, "levels must be a String or Symbol" end
|
115
|
+
end
|
116
|
+
|
117
|
+
def level_num( level )
|
118
|
+
l = levelify level
|
119
|
+
case l
|
120
|
+
when 'all': 0
|
121
|
+
when 'off': LEVELS.length
|
122
|
+
else LEVELS[l] end
|
123
|
+
end
|
124
|
+
# :startdoc:
|
125
|
+
end
|
126
|
+
|
127
|
+
end # module Logging
|
128
|
+
|
129
|
+
# EOF
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# $Id: appender.rb 12 2007-01-14 20:03:40Z tim_pease $
|
2
|
+
|
3
|
+
require 'sync'
|
4
|
+
require 'logging'
|
5
|
+
require 'logging/logger'
|
6
|
+
require 'logging/layout'
|
7
|
+
require 'logging/layouts/basic'
|
8
|
+
|
9
|
+
module Logging
|
10
|
+
|
11
|
+
#
|
12
|
+
# The +Appender+ class is provides methods for appending log events to a
|
13
|
+
# logging destination. The log events are formatted into strings using a
|
14
|
+
# Layout.
|
15
|
+
#
|
16
|
+
# All other Appenders inherit from this class which provides stub methods.
|
17
|
+
# Each subclass should provide a +write+ method that will write log
|
18
|
+
# messages to the logging destination.
|
19
|
+
#
|
20
|
+
# A private +sync+ method is provided for use by subclasses. It is used to
|
21
|
+
# synchronize writes to the logging destination, and can be used by
|
22
|
+
# subclasses to synchronize the closing or flushing of the logging
|
23
|
+
# destination.
|
24
|
+
#
|
25
|
+
class Appender
|
26
|
+
|
27
|
+
attr_reader :name, :layout, :level
|
28
|
+
|
29
|
+
#
|
30
|
+
# call-seq:
|
31
|
+
# Appender.new( name )
|
32
|
+
# Appender.new( name, :layout => layout )
|
33
|
+
#
|
34
|
+
# Creates a new appender using the given name. If no Layout is specified,
|
35
|
+
# then a Basic layout will be used. Any logging header supplied by the
|
36
|
+
# layout will be written to the logging destination when the Appender is
|
37
|
+
# created.
|
38
|
+
#
|
39
|
+
def initialize( name, opts = {} )
|
40
|
+
@name = name.to_s
|
41
|
+
@closed = false
|
42
|
+
@level = 0
|
43
|
+
self.layout = opts[:layout] if opts.include? :layout
|
44
|
+
@layout ||= ::Logging::Layouts::Basic.new
|
45
|
+
|
46
|
+
@sync = Sync.new
|
47
|
+
sync {write(@layout.header)}
|
48
|
+
|
49
|
+
::Logging::Appender[@name] = self
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# call-seq:
|
54
|
+
# append( event )
|
55
|
+
#
|
56
|
+
# Write the given _event_ to the logging destination. The log event will
|
57
|
+
# be processed through the Layout associated with the Appender.
|
58
|
+
#
|
59
|
+
def append( event )
|
60
|
+
if @closed
|
61
|
+
raise RuntimeError,
|
62
|
+
"appender '<#{self.class.name}: #{@name}>' is closed"
|
63
|
+
end
|
64
|
+
|
65
|
+
sync {write(@layout.format(event))} unless @level > event.level
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# call-seq:
|
71
|
+
# appender << string
|
72
|
+
#
|
73
|
+
# Write the given _string_ to the logging destination "as is" -- no
|
74
|
+
# layout formatting will be performed.
|
75
|
+
#
|
76
|
+
def <<( str )
|
77
|
+
if @closed
|
78
|
+
raise RuntimeError,
|
79
|
+
"appender '<#{self.class.name}: #{@name}>' is closed"
|
80
|
+
end
|
81
|
+
|
82
|
+
sync {write(str)}
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# call-seq:
|
88
|
+
# level = :all
|
89
|
+
#
|
90
|
+
# Set the level for this appender; log events below this level will be
|
91
|
+
# ignored by this appender. The level can be either a +String+, a
|
92
|
+
# +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
|
93
|
+
# the case.
|
94
|
+
#
|
95
|
+
# There are two special levels -- "all" and "off". The former will
|
96
|
+
# enable recording of all log events. The latter will disable the
|
97
|
+
# recording of all events.
|
98
|
+
#
|
99
|
+
# Example:
|
100
|
+
#
|
101
|
+
# appender.level = :debug
|
102
|
+
# appender.level = "INFO"
|
103
|
+
# appender.level = 4
|
104
|
+
# appender.level = 'off'
|
105
|
+
# appender.level = :all
|
106
|
+
#
|
107
|
+
# These prodcue an +ArgumentError+
|
108
|
+
#
|
109
|
+
# appender.level = Object
|
110
|
+
# appender.level = -1
|
111
|
+
# appender.level = 1_000_000_000_000
|
112
|
+
#
|
113
|
+
def level=( level )
|
114
|
+
lvl = case level
|
115
|
+
when String, Symbol: ::Logging::level_num(level)
|
116
|
+
when Fixnum: level
|
117
|
+
when nil: 0
|
118
|
+
else
|
119
|
+
raise ArgumentError,
|
120
|
+
"level must be a String, Symbol, or Integer"
|
121
|
+
end
|
122
|
+
if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
|
123
|
+
raise ArgumentError, "unknown level was given '#{level}'"
|
124
|
+
end
|
125
|
+
|
126
|
+
@level = lvl
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# call-seq
|
131
|
+
# appender.layout = Logging::Layouts::Basic.new
|
132
|
+
#
|
133
|
+
# Sets the layout to be used by this appender.
|
134
|
+
#
|
135
|
+
def layout=( layout )
|
136
|
+
unless layout.kind_of? ::Logging::Layout
|
137
|
+
raise TypeError,
|
138
|
+
"#{layout.inspect} is not a kind of 'Logging::Layout'"
|
139
|
+
end
|
140
|
+
@layout = layout
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# call-seq:
|
145
|
+
# close( footer = true )
|
146
|
+
#
|
147
|
+
# Close the appender and writes the layout footer to the logging
|
148
|
+
# destination if the _footer_ flag is set to +true+. Log events will
|
149
|
+
# no longer be written to the logging destination after the appender
|
150
|
+
# is closed.
|
151
|
+
#
|
152
|
+
def close( footer = true )
|
153
|
+
return self if @closed
|
154
|
+
@closed = true
|
155
|
+
sync {write(@layout.footer)} if footer
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# call-seq:
|
161
|
+
# closed?
|
162
|
+
#
|
163
|
+
# Returns +true+ if the appender has been closed; returns +false+
|
164
|
+
# otherwise. When an appender is closed, no more log events can be
|
165
|
+
# written to the logging destination.
|
166
|
+
#
|
167
|
+
def closed?( ) @closed end
|
168
|
+
|
169
|
+
#
|
170
|
+
# call-seq:
|
171
|
+
# flush
|
172
|
+
#
|
173
|
+
# Call +flush+ to force an appender to write out any buffered log events.
|
174
|
+
# Similar to IO#flush, so use in a similar fashion.
|
175
|
+
#
|
176
|
+
def flush( ) self end
|
177
|
+
|
178
|
+
|
179
|
+
private
|
180
|
+
#
|
181
|
+
# call-seq:
|
182
|
+
# write( str )
|
183
|
+
#
|
184
|
+
# Writes the given string to the logging destination. Subclasses should
|
185
|
+
# provide an implementation of this method.
|
186
|
+
#
|
187
|
+
def write( str ) nil end
|
188
|
+
|
189
|
+
#
|
190
|
+
# call-seq:
|
191
|
+
# sync { block }
|
192
|
+
#
|
193
|
+
# Obtains an exclusive lock, runs the block, and releases the lock when
|
194
|
+
# the block completes. This method is re-entrant so that a single thread
|
195
|
+
# can call +sync+ multiple times without hanging the thread.
|
196
|
+
#
|
197
|
+
def sync
|
198
|
+
if Thread.current == @sync.sync_ex_locker then yield
|
199
|
+
else @sync.synchronize(:EX) {yield} end
|
200
|
+
end
|
201
|
+
|
202
|
+
end # class Appender
|
203
|
+
end # module Logging
|
204
|
+
|
205
|
+
# EOF
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# $Id: console.rb 2 2007-01-09 18:10:50Z tim_pease $
|
2
|
+
|
3
|
+
require 'logging/appenders/io'
|
4
|
+
|
5
|
+
module Logging
|
6
|
+
module Appenders
|
7
|
+
|
8
|
+
#
|
9
|
+
# This class provides an Appender that can write to STDOUT.
|
10
|
+
#
|
11
|
+
class StdOut< ::Logging::Appenders::IO
|
12
|
+
|
13
|
+
#
|
14
|
+
# call-seq:
|
15
|
+
# StdOut.new
|
16
|
+
# StdOut.new( :layout => layout )
|
17
|
+
#
|
18
|
+
# Creates a new StdOut Appender. The name 'stdout' will always be used for
|
19
|
+
# this appender.
|
20
|
+
#
|
21
|
+
def initialize( opts = {} )
|
22
|
+
super('stdout', STDOUT, opts)
|
23
|
+
end
|
24
|
+
end # class StdOut
|
25
|
+
|
26
|
+
#
|
27
|
+
# This class provides an Appender that can write to STDERR.
|
28
|
+
#
|
29
|
+
class StdErr< ::Logging::Appenders::IO
|
30
|
+
|
31
|
+
#
|
32
|
+
# call-seq:
|
33
|
+
# StdErr.new
|
34
|
+
# StdErr.new( :layout => layout )
|
35
|
+
#
|
36
|
+
# Creates a new StdErr Appender. The name 'stderr' will always be used for
|
37
|
+
# this appender.
|
38
|
+
#
|
39
|
+
def initialize( opts = {} )
|
40
|
+
super('stderr', STDERR, opts)
|
41
|
+
end
|
42
|
+
end # class StdErr
|
43
|
+
|
44
|
+
end # module Appenders
|
45
|
+
end # module Logging
|
46
|
+
|
47
|
+
# EOF
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# $Id: file.rb 2 2007-01-09 18:10:50Z tim_pease $
|
2
|
+
|
3
|
+
require 'logging/appenders/io'
|
4
|
+
|
5
|
+
module Logging
|
6
|
+
module Appenders
|
7
|
+
|
8
|
+
#
|
9
|
+
# This class provides an Appender that can write to a File.
|
10
|
+
#
|
11
|
+
class File < ::Logging::Appenders::IO
|
12
|
+
|
13
|
+
#
|
14
|
+
# call-seq:
|
15
|
+
# File.new( filename )
|
16
|
+
# File.new( filename, :truncate => true )
|
17
|
+
# File.new( filename, :layout => layout )
|
18
|
+
#
|
19
|
+
# Creates a new File Appender that will use the given _filename_ as the
|
20
|
+
# logging destination. If the file does not already exist it will be
|
21
|
+
# created. If the :truncate option is set to +true+ then the file will be
|
22
|
+
# truncated before writing begins; otherwise, log messages will be appened
|
23
|
+
# to the file.
|
24
|
+
#
|
25
|
+
def initialize( filename, opts = {} )
|
26
|
+
mode = opts.delete(:truncate) ? 'w' : 'a'
|
27
|
+
|
28
|
+
if ::File.exist?(filename)
|
29
|
+
if not ::File.file?(filename)
|
30
|
+
raise StandardError, "#{filename} is not a regular file"
|
31
|
+
elsif not ::File.writable?(filename)
|
32
|
+
raise StandardError, "#{filename} is not writeable"
|
33
|
+
end
|
34
|
+
elsif not ::File.writable?(::File.dirname(filename))
|
35
|
+
raise StandardError, "#{::File.dirname(filename)} is not writable"
|
36
|
+
end
|
37
|
+
|
38
|
+
super(filename, ::File.new(filename, mode), opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
end # class FileAppender
|
42
|
+
end # module Appenders
|
43
|
+
end # module Logging
|
44
|
+
|
45
|
+
# EOF
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# $Id: io.rb 10 2007-01-12 18:57:07Z tim_pease $
|
2
|
+
|
3
|
+
require 'logging/appender'
|
4
|
+
|
5
|
+
module Logging
|
6
|
+
module Appenders
|
7
|
+
|
8
|
+
#
|
9
|
+
# This class provides an Appender that can write to any IO stream
|
10
|
+
# configured for writing.
|
11
|
+
#
|
12
|
+
class IO < ::Logging::Appender
|
13
|
+
|
14
|
+
#
|
15
|
+
# call-seq:
|
16
|
+
# IO.new( name, io )
|
17
|
+
# IO.new( name, io, :layout => layout )
|
18
|
+
#
|
19
|
+
# Creates a new IO Appender using the given name that will use the _io_
|
20
|
+
# stream as the logging destination.
|
21
|
+
#
|
22
|
+
def initialize( name, io, opts = {} )
|
23
|
+
unless io.respond_to? :print
|
24
|
+
raise TypeError, "expecting an IO object but got '#{io.class.name}'"
|
25
|
+
end
|
26
|
+
|
27
|
+
@io = io
|
28
|
+
@io.sync = true
|
29
|
+
|
30
|
+
super(name, opts)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# call-seq:
|
35
|
+
# close( footer = true )
|
36
|
+
#
|
37
|
+
# Close the appender and writes the layout footer to the logging
|
38
|
+
# destination if the _footer_ flag is set to +true+. Log events will
|
39
|
+
# no longer be written to the logging destination after the appender
|
40
|
+
# is closed.
|
41
|
+
#
|
42
|
+
def close( *args )
|
43
|
+
return self if @io.nil?
|
44
|
+
sync do
|
45
|
+
super(*args)
|
46
|
+
@io.close unless [STDIN, STDERR, STDOUT].include?(@io)
|
47
|
+
@io = nil
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# call-seq:
|
54
|
+
# flush
|
55
|
+
#
|
56
|
+
# Call +flush+ to force an appender to write out any buffered log events.
|
57
|
+
# Similar to IO#flush, so use in a similar fashion.
|
58
|
+
#
|
59
|
+
def flush
|
60
|
+
return self if @io.nil?
|
61
|
+
@io.flush
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
private
|
67
|
+
#
|
68
|
+
# call-seq:
|
69
|
+
# write( str )
|
70
|
+
#
|
71
|
+
# Writes the given string to the IO stream. If an +IOError+ is detected,
|
72
|
+
# than this appender will be closed and the error reported.
|
73
|
+
#
|
74
|
+
def write( str )
|
75
|
+
begin
|
76
|
+
@io.print str
|
77
|
+
rescue IOError
|
78
|
+
close false
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end # class IO
|
84
|
+
end # module Appenders
|
85
|
+
end # module Logging
|
86
|
+
|
87
|
+
# EOF
|