logging 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.
data/README.txt ADDED
@@ -0,0 +1,11 @@
1
+ --
2
+ $Id: README.txt 12 2007-01-14 20:03:40Z tim_pease $
3
+ ++
4
+
5
+ = Logging
6
+
7
+ obtaining a logger
8
+ where to log
9
+ log statements
10
+ changing the log level
11
+
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