filterfish-logging 0.9.8

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.
Files changed (55) hide show
  1. data/History.txt +176 -0
  2. data/Manifest.txt +54 -0
  3. data/README.txt +93 -0
  4. data/Rakefile +28 -0
  5. data/data/logging.yaml +63 -0
  6. data/lib/logging.rb +288 -0
  7. data/lib/logging/appender.rb +257 -0
  8. data/lib/logging/appenders/console.rb +43 -0
  9. data/lib/logging/appenders/email.rb +131 -0
  10. data/lib/logging/appenders/file.rb +55 -0
  11. data/lib/logging/appenders/growl.rb +182 -0
  12. data/lib/logging/appenders/io.rb +81 -0
  13. data/lib/logging/appenders/rolling_file.rb +293 -0
  14. data/lib/logging/appenders/syslog.rb +202 -0
  15. data/lib/logging/config/yaml_configurator.rb +197 -0
  16. data/lib/logging/layout.rb +103 -0
  17. data/lib/logging/layouts/basic.rb +35 -0
  18. data/lib/logging/layouts/pattern.rb +292 -0
  19. data/lib/logging/log_event.rb +50 -0
  20. data/lib/logging/logger.rb +388 -0
  21. data/lib/logging/repository.rb +151 -0
  22. data/lib/logging/root_logger.rb +60 -0
  23. data/lib/logging/utils.rb +44 -0
  24. data/tasks/ann.rake +78 -0
  25. data/tasks/bones.rake +21 -0
  26. data/tasks/gem.rake +106 -0
  27. data/tasks/manifest.rake +49 -0
  28. data/tasks/notes.rake +22 -0
  29. data/tasks/post_load.rake +37 -0
  30. data/tasks/rdoc.rake +49 -0
  31. data/tasks/rubyforge.rake +57 -0
  32. data/tasks/setup.rb +253 -0
  33. data/tasks/svn.rake +45 -0
  34. data/tasks/test.rake +38 -0
  35. data/test/appenders/test_console.rb +40 -0
  36. data/test/appenders/test_email.rb +167 -0
  37. data/test/appenders/test_file.rb +94 -0
  38. data/test/appenders/test_growl.rb +115 -0
  39. data/test/appenders/test_io.rb +113 -0
  40. data/test/appenders/test_rolling_file.rb +187 -0
  41. data/test/appenders/test_syslog.rb +192 -0
  42. data/test/benchmark.rb +88 -0
  43. data/test/config/test_yaml_configurator.rb +41 -0
  44. data/test/layouts/test_basic.rb +44 -0
  45. data/test/layouts/test_pattern.rb +173 -0
  46. data/test/setup.rb +66 -0
  47. data/test/test_appender.rb +162 -0
  48. data/test/test_layout.rb +85 -0
  49. data/test/test_log_event.rb +81 -0
  50. data/test/test_logger.rb +589 -0
  51. data/test/test_logging.rb +250 -0
  52. data/test/test_repository.rb +123 -0
  53. data/test/test_root_logger.rb +82 -0
  54. data/test/test_utils.rb +48 -0
  55. metadata +126 -0
@@ -0,0 +1,35 @@
1
+ # $Id$
2
+
3
+ module Logging
4
+ module Layouts
5
+
6
+ # The +Basic+ layout class provides methods for simple formatting of log
7
+ # events. The resulting string follows the format below.
8
+ #
9
+ # LEVEL LoggerName : log message
10
+ #
11
+ # _LEVEL_ is the log level of the event. _LoggerName_ is the name of the
12
+ # logger that generated the event. <em>log message</em> is the message
13
+ # or object that was passed to the logger. If multiple message or objects
14
+ # were passed to the logger then each will be printed on its own line with
15
+ # the format show above.
16
+ #
17
+ class Basic < ::Logging::Layout
18
+
19
+ # call-seq:
20
+ # format( event )
21
+ #
22
+ # Returns a string representation of the given loggging _event_. See the
23
+ # class documentation for details about the formatting used.
24
+ #
25
+ def format( event )
26
+ obj = format_obj(event.data)
27
+ sprintf("%*s %s : %s\n", ::Logging::MAX_LEVEL_LENGTH,
28
+ ::Logging::LNAMES[event.level], event.logger, obj)
29
+ end
30
+
31
+ end # class Basic
32
+ end # module Layouts
33
+ end # module Logging
34
+
35
+ # EOF
@@ -0,0 +1,292 @@
1
+ # $Id$
2
+
3
+ module Logging
4
+ module Layouts
5
+
6
+ # A flexible layout configurable with pattern string.
7
+ #
8
+ # The goal of this class is to format a LogEvent and return the results as
9
+ # a String. The results depend on the conversion pattern.
10
+ #
11
+ # The conversion pattern is closely related to the conversion pattern of
12
+ # the sprintf function. A conversion pattern is composed of literal text
13
+ # and format control expressions called conversion specifiers.
14
+ #
15
+ # You are free to insert any literal text within the conversion pattern.
16
+ #
17
+ # Each conversion specifier starts with a percent sign (%) and is followed
18
+ # by optional format modifiers and a conversion character. The conversion
19
+ # character specifies the type of data, e.g. logger, level, date, thread
20
+ # ID. The format modifiers control such things as field width, padding,
21
+ # left and right justification. The following is a simple example.
22
+ #
23
+ # Let the conversion pattern be "%-5l [%c]: %m\n" and assume that the
24
+ # logging environment was set to use a Pattern layout. Then the statements
25
+ #
26
+ # root = Logging::Logger[:root]
27
+ # root.debug("Message 1")
28
+ # root.warn("Message 2")
29
+ #
30
+ # would yield the output
31
+ #
32
+ # DEBUG [root]: Message 1
33
+ # WARN [root]: Message 2
34
+ #
35
+ # Note that there is no explicit separator between text and conversion
36
+ # specifiers. The pattern parser knows when it has reached the end of a
37
+ # conversion specifier when it reads a conversion character. In the example
38
+ # above the conversion specifier %-5l means the level of the logging event
39
+ # should be left justified to a width of five characters. The recognized
40
+ # conversion characters are
41
+ #
42
+ # [c] Used to output the name of the logger that generated the log
43
+ # event.
44
+ # [d] Used to output the date of the log event. The format of the
45
+ # date is specified using the :date_pattern option when the Layout
46
+ # is created. ISO8601 format is assumed if not date pattern is given.
47
+ # [F] Used to output the file name where the logging request was issued.
48
+ # [l] Used to output the level of the log event.
49
+ # [L] Used to output the line number where the logging request was
50
+ # issued.
51
+ # [m] Used to output the application supplied message associated with
52
+ # the log event.
53
+ # [M] Used to output the method name where the logging request was
54
+ # issued.
55
+ # [p] Used to output the process ID of the currently running program.
56
+ # [r] Used to output the number of milliseconds elapsed from the
57
+ # construction of the Layout until creation of the log event.
58
+ # [t] Used to output the object ID of the thread that generated the
59
+ # log event.
60
+ # [%] The sequence '%%' outputs a single percent sign.
61
+ #
62
+ # The directives F, L, and M will only work if the Logger generating the
63
+ # events is configured to generate tracing information. If this is not
64
+ # the case these fields will always be empty.
65
+ #
66
+ # By default the relevant information is output as is. However, with the
67
+ # aid of format modifiers it is possible to change the minimum field width,
68
+ # the maximum field width and justification.
69
+ #
70
+ # The optional format modifier is placed between the percent sign and the
71
+ # conversion character.
72
+ #
73
+ # The first optional format modifier is the left justification flag which
74
+ # is just the minus (-) character. Then comes the optional minimum field
75
+ # width modifier. This is a decimal constant that represents the minimum
76
+ # number of characters to output. If the data item requires fewer
77
+ # characters, it is padded on either the left or the right until the
78
+ # minimum width is reached. The default is to pad on the left (right
79
+ # justify) but you can specify right padding with the left justification
80
+ # flag. The padding character is space. If the data item is larger than the
81
+ # minimum field width, the field is expanded to accommodate the data. The
82
+ # value is never truncated.
83
+ #
84
+ # This behavior can be changed using the maximum field width modifier which
85
+ # is designated by a period followed by a decimal constant. If the data
86
+ # item is longer than the maximum field, then the extra characters are
87
+ # removed from the end of the data item.
88
+ #
89
+ # Below are various format modifier examples for the category conversion
90
+ # specifier.
91
+ #
92
+ # [%20c] Left pad with spaces if the logger name is less than 20
93
+ # characters long
94
+ # [%-20c] Right pad with spaces if the logger name is less than 20
95
+ # characters long
96
+ # [%.30c] Truncates the logger name if it is longer than 30 characters
97
+ # [%20.30c] Left pad with spaces if the logger name is shorter than
98
+ # 20 characters. However, if the logger name is longer than
99
+ # 30 characters, then truncate the name.
100
+ # [%-20.30c] Right pad with spaces if the logger name is shorter than
101
+ # 20 characters. However, if the logger name is longer than
102
+ # 30 characters, then truncate the name.
103
+ #
104
+ # Below are examples of some conversion patterns.
105
+ #
106
+ # %.1l, [%d] %5l -- %c: %m\n
107
+ #
108
+ # This is how the Logger class in the Ruby standard library formats
109
+ # messages. The main difference will be in the date format (the Pattern
110
+ # Layout uses the ISO8601 date format). Set the :date_method on the
111
+ # Pattern Layout to be 'to_s' and then the date formats will agree.
112
+ #
113
+ class Pattern < ::Logging::Layout
114
+
115
+ # :stopdoc:
116
+
117
+ # Arguments to sprintf keyed to directive letters
118
+ DIRECTIVE_TABLE = {
119
+ 'c' => 'event.logger',
120
+ 'd' => 'format_date',
121
+ 'F' => 'event.file',
122
+ 'l' => '::Logging::LNAMES[event.level]',
123
+ 'L' => 'event.line',
124
+ 'm' => 'format_obj(event.data)',
125
+ 'M' => 'event.method',
126
+ 'p' => 'Process.pid',
127
+ 'r' => 'Integer((Time.now-@created_at)*1000).to_s',
128
+ 't' => 'Thread.current.object_id.to_s',
129
+ '%' => :placeholder
130
+ }
131
+
132
+ # Matches the first directive encountered and the stuff around it.
133
+ #
134
+ # * $1 is the stuff before directive or "" if not applicable
135
+ # * $2 is the %#.# match within directive group
136
+ # * $3 is the directive letter
137
+ # * $4 is the stuff after the directive or "" if not applicable
138
+ DIRECTIVE_RGXP = %r/([^%]*)(?:(%-?\d*(?:\.\d+)?)([a-zA-Z%]))?(.*)/m
139
+
140
+ # default date format
141
+ ISO8601 = "%Y-%m-%d %H:%M:%S"
142
+
143
+ # call-seq:
144
+ # Pattern.create_date_format_methods( pf )
145
+ #
146
+ # This method will create the +date_format+ method in the given Pattern
147
+ # Layout _pf_ based on the configured date patten and/or date method
148
+ # specified by the user.
149
+ #
150
+ def self.create_date_format_methods( pf )
151
+ code = "undef :format_date if method_defined? :format_date\n"
152
+ code << "def format_date\n"
153
+ if pf.date_method.nil?
154
+ if pf.date_pattern =~ %r/%s/
155
+ code << <<-CODE
156
+ now = Time.now
157
+ dp = '#{pf.date_pattern}'.gsub('%s','%06d' % now.usec)
158
+ now.strftime dp
159
+ CODE
160
+ else
161
+ code << "Time.now.strftime '#{pf.date_pattern}'\n"
162
+ end
163
+ else
164
+ code << "Time.now.#{pf.date_method}\n"
165
+ end
166
+ code << "end\n"
167
+
168
+ pf.meta_eval code
169
+ end
170
+
171
+ # call-seq:
172
+ # Pattern.create_format_method( pf )
173
+ #
174
+ # This method will create the +format+ method in the given Pattern
175
+ # Layout _pf_ based on the configured format pattern specified by the
176
+ # user.
177
+ #
178
+ def self.create_format_method( pf )
179
+ # Create the format(event) method
180
+ code = "undef :format if method_defined? :format\n"
181
+ code << "def format( event )\nsprintf(\""
182
+ pattern = pf.pattern.dup
183
+ args = []
184
+
185
+ while true
186
+ m = DIRECTIVE_RGXP.match(pattern)
187
+ code << m[1] unless m[1].empty?
188
+
189
+ case m[3]
190
+ when '%'; code << '%%'
191
+ when *DIRECTIVE_TABLE.keys
192
+ code << m[2] + 's'
193
+ args << DIRECTIVE_TABLE[m[3]]
194
+ when nil; break
195
+ else
196
+ raise ArgumentError, "illegal format character - '#{m[3]}'"
197
+ end
198
+
199
+ break if m[4].empty?
200
+ pattern = m[4]
201
+ end
202
+
203
+ code << '"'
204
+ code << ', ' + args.join(', ') unless args.empty?
205
+ code << ")\n"
206
+ code << "end\n"
207
+
208
+ pf.meta_eval code
209
+ end
210
+ # :startdoc:
211
+
212
+ # call-seq:
213
+ # Pattern.new( opts )
214
+ #
215
+ # Creates a new Pattern layout using the following options.
216
+ #
217
+ # :pattern => "[%d] %-5l -- %c : %m\n"
218
+ # :date_pattern => "%Y-%m-%d %H:%M:%S"
219
+ # :date_method => 'usec' or 'to_s'
220
+ #
221
+ # If used, :date_method will supersede :date_pattern.
222
+ #
223
+ def initialize( opts = {} )
224
+ super
225
+ @created_at = Time.now
226
+
227
+ @date_pattern = opts.getopt(:date_pattern)
228
+ @date_method = opts.getopt(:date_method)
229
+ @date_pattern = ISO8601 if @date_pattern.nil? and @date_method.nil?
230
+
231
+ @pattern = opts.getopt(:pattern,
232
+ "[%d] %-#{::Logging::MAX_LEVEL_LENGTH}l -- %c : %m\n")
233
+
234
+ Pattern.create_date_format_methods(self)
235
+ Pattern.create_format_method(self)
236
+ end
237
+
238
+ attr_reader :pattern, :date_pattern, :date_method
239
+
240
+ # call-seq:
241
+ # appender.pattern = "[%d] %-5l -- %c : %m\n"
242
+ #
243
+ # Set the message formatting pattern to be used by the layout.
244
+ #
245
+ def pattern=( var )
246
+ @pattern = var
247
+ Pattern.create_format_method(self)
248
+ end
249
+
250
+ # call-seq:
251
+ # appender.date_pattern = "%Y-%m-%d %H:%M:%S"
252
+ #
253
+ # Set the date formatting pattern to be used when outputting timestamps
254
+ # in the log messages.
255
+ #
256
+ def date_pattern=( var )
257
+ @date_pattern = var
258
+ Pattern.create_date_format_methods(self)
259
+ end
260
+
261
+ # call-seq:
262
+ # appender.date_method = 'to_s'
263
+ # appender.date_method = :usec
264
+ #
265
+ # Set the date method to be used when outputting timestamps in the log
266
+ # messages. If a date method is configured, the output of that method
267
+ # will be used in leu of the date pattern.
268
+ #
269
+ def date_method=( var )
270
+ @date_method = var
271
+ Pattern.create_date_format_methods(self)
272
+ end
273
+
274
+ # :stopdoc:
275
+
276
+ # call-seq:
277
+ # meta_eval( code )
278
+ #
279
+ # Evaluates the given string of _code_ if the singleton class of this
280
+ # Pattern Layout object.
281
+ #
282
+ def meta_eval( code )
283
+ meta = class << self; self end
284
+ meta.class_eval code
285
+ end
286
+ # :startdoc:
287
+
288
+ end # class Pattern
289
+ end # module Layouts
290
+ end # module Logging
291
+
292
+ # EOF
@@ -0,0 +1,50 @@
1
+ # $Id$
2
+
3
+ module Logging
4
+
5
+ # This class defines a logging event.
6
+ #
7
+ class LogEvent
8
+
9
+ # :stopdoc:
10
+
11
+ # Regular expression used to parse out caller information
12
+ #
13
+ # * $1 == filename
14
+ # * $2 == line number
15
+ # * $3 == method name (might be nil)
16
+ CALLER_RGXP = %r/([\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
17
+ # :startdoc:
18
+
19
+ # call-seq:
20
+ # LogEvent.new( logger, level, [data], trace )
21
+ #
22
+ # Creates a new log event with the given _logger_ name, numeric _level_,
23
+ # array of _data_ from the user to be logged, and boolean _trace_ flag.
24
+ # If the _trace_ flag is set to +true+ then Kernel::caller will be
25
+ # invoked to get the execution trace of the logging method.
26
+ #
27
+ def initialize( logger, level, data, trace )
28
+ @logger = logger
29
+ @level = level
30
+ @data = data
31
+ @file = @line = @method = ''
32
+
33
+ if trace
34
+ t = Kernel.caller[2]
35
+ return if t.nil?
36
+
37
+ m = CALLER_RGXP.match(t)
38
+ @file = m[1]
39
+ @line = m[2]
40
+ @method = m[3] unless m[3].nil?
41
+ end
42
+ end
43
+
44
+ attr_accessor :logger, :level, :data
45
+ attr_reader :file, :line, :method
46
+
47
+ end # class LogEvent
48
+ end # module Logging
49
+
50
+ # EOF
@@ -0,0 +1,388 @@
1
+ # $Id$
2
+
3
+ require 'thread'
4
+
5
+ module Logging
6
+
7
+ # The +Logger+ class is the primary interface to the +Logging+ framework.
8
+ # It provides the logging methods that will be called from user methods,
9
+ # and it generates logging events that are sent to the appenders (the
10
+ # appenders take care of sending the log events to the logging
11
+ # destinations -- files, sockets, etc).
12
+ #
13
+ # +Logger+ instances are obtained from the +Repository+ and should
14
+ # not be directly created by users.
15
+ #
16
+ # Example:
17
+ #
18
+ # log = Logging::Logger['my logger']
19
+ # log.add_appenders( Logging::Appender.stdout ) # append to STDOUT
20
+ # log.level = :info # log 'info' and above
21
+ #
22
+ # log.info 'starting foo operation'
23
+ # ...
24
+ # log.info 'finishing foo operation'
25
+ # ...
26
+ # log.fatal 'unknown exception', exception
27
+ #
28
+ class Logger
29
+
30
+ @mutex = Mutex.new # :nodoc:
31
+
32
+ class << self
33
+
34
+ # call-seq:
35
+ # Logger.root
36
+ #
37
+ # Returns the root logger.
38
+ #
39
+ def root
40
+ ::Logging::Repository.instance[:root]
41
+ end
42
+
43
+ # :stopdoc:
44
+
45
+ # Overrides the new method such that only one Logger will be created
46
+ # for any given logger name.
47
+ #
48
+ def new( *args )
49
+ return super if args.empty?
50
+
51
+ repo = ::Logging::Repository.instance
52
+ name = repo.to_key(args.shift)
53
+
54
+ @mutex.synchronize do
55
+ logger = repo[name]
56
+ if logger.nil?
57
+ logger = super(name, *args)
58
+ repo[name] = logger
59
+ end
60
+ logger
61
+ end
62
+ end
63
+ alias :[] :new
64
+
65
+ # This is where the actual logging methods are defined. Two methods
66
+ # are created for each log level. The first is a query method used to
67
+ # determine if that perticular logging level is enabled. The second is
68
+ # the actual logging method that accepts a list of objects to be
69
+ # logged or a block. If a block is given, then the object returned
70
+ # from the block will be logged.
71
+ #
72
+ # Example
73
+ #
74
+ # log = Logging::Logger['my logger']
75
+ # log.level = :warn
76
+ #
77
+ # log.info? # => false
78
+ # log.warn? # => true
79
+ # log.warn 'this is your last warning'
80
+ # log.fatal 'I die!', exception
81
+ #
82
+ # log.debug do
83
+ # # expensive method to construct log message
84
+ # msg
85
+ # end
86
+ #
87
+ def define_log_methods( logger )
88
+ ::Logging::LEVELS.each do |name,num|
89
+ code = "undef :#{name} if method_defined? :#{name}\n"
90
+ code << "undef :#{name}? if method_defined? :#{name}?\n"
91
+
92
+ if logger.level > num
93
+ code << <<-CODE
94
+ def #{name}?( ) false end
95
+ def #{name}( data = nil ) false end
96
+ CODE
97
+ else
98
+ code << <<-CODE
99
+ def #{name}?( ) true end
100
+ def #{name}( data = nil )
101
+ data = yield if block_given?
102
+ log_event(::Logging::LogEvent.new(@name, #{num}, data, @trace))
103
+ true
104
+ end
105
+ CODE
106
+ end
107
+
108
+ logger.meta_eval code
109
+ end
110
+ end
111
+ # :startdoc:
112
+
113
+ end # class << self
114
+
115
+ attr_reader :level, :name, :parent, :additive, :trace
116
+
117
+ # call-seq:
118
+ # Logger.new( name )
119
+ # Logger[name]
120
+ #
121
+ # Returns the logger identified by _name_.
122
+ #
123
+ # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to
124
+ # retrieve the logger. When _name_ is a +Class+ the class name will be
125
+ # used to retrieve the logger. When _name_ is an object the name of the
126
+ # object's class will be used to retrieve the logger.
127
+ #
128
+ # Example:
129
+ #
130
+ # obj = MyClass.new
131
+ #
132
+ # log1 = Logger.new(obj)
133
+ # log2 = Logger.new(MyClass)
134
+ # log3 = Logger['MyClass']
135
+ #
136
+ # log1.object_id == log2.object_id # => true
137
+ # log2.object_id == log3.object_id # => true
138
+ #
139
+ def initialize( name )
140
+ case name
141
+ when String
142
+ raise(ArgumentError, "logger must have a name") if name.empty?
143
+ else raise(ArgumentError, "logger name must be a String") end
144
+
145
+ repo = ::Logging::Repository.instance
146
+ @name = name
147
+ @parent = repo.parent(name)
148
+ @appenders = []
149
+ @additive = true
150
+ @trace = false
151
+ self.level = @parent.level
152
+
153
+ repo.children(name).each {|c| c.parent = self}
154
+ end
155
+
156
+ # call-seq:
157
+ # log <=> other
158
+ #
159
+ # Compares this logger by name to another logger. The normal return codes
160
+ # for +String+ objects apply.
161
+ #
162
+ def <=>( other )
163
+ case other
164
+ when self; 0
165
+ when ::Logging::RootLogger; 1
166
+ when ::Logging::Logger; @name <=> other.name
167
+ else raise ArgumentError, 'expecting a Logger instance' end
168
+ end
169
+
170
+ # call-seq:
171
+ # log << "message"
172
+ #
173
+ # Log the given message without any formatting and without performing any
174
+ # level checks. The message is logged to all appenders. The message is
175
+ # passed up the logger tree if this logger's additivity is +true+.
176
+ #
177
+ def <<( msg )
178
+ @appenders.each {|a| a << msg}
179
+ @parent << msg if @additive
180
+ end
181
+
182
+ # call-seq:
183
+ # add( severity, message = nil ) {block}
184
+ #
185
+ # Log a message if the given severity is high enough. This is the generic
186
+ # logging method. Users will be more inclined to use #debug, #info, #warn,
187
+ # #error, and #fatal.
188
+ #
189
+ # <b>Message format</b>: +message+ can be any object, but it has to be
190
+ # converted to a String in order to log it. The Logging::format_as
191
+ # method is used to determine how objects chould be converted to
192
+ # strings. Generally, +inspect+ is used.
193
+ #
194
+ # A special case is an +Exception+ object, which will be printed in
195
+ # detail, including message, class, and backtrace.
196
+ #
197
+ # If a _message_ is not given, then the return value from the block is
198
+ # used as the message to log. This is useful when creating the actual
199
+ # message is an expensive operation. This allows the logger to check the
200
+ # severity against the configured level before actually constructing the
201
+ # message.
202
+ #
203
+ # This method returns +true+ if the message was logged, and +false+ is
204
+ # returned if the message was not logged.
205
+ #
206
+ def add( lvl, data = nil )
207
+ lvl = Integer(lvl)
208
+ return false if lvl < level
209
+
210
+ data = yield if block_given?
211
+ log_event(::Logging::LogEvent.new(@name, lvl, data, @trace))
212
+ true
213
+ end
214
+
215
+ # call-seq:
216
+ # additive = true
217
+ #
218
+ # Sets the additivity of the logger. Acceptable values are +true+,
219
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
220
+ # change the additivity
221
+ #
222
+ def additive=( val )
223
+ @additive = case val
224
+ when true, 'true'; true
225
+ when false, 'false'; false
226
+ when nil; @additive
227
+ else raise ArgumentError, 'expecting a boolean' end
228
+ end
229
+
230
+ # call-seq:
231
+ # trace = true
232
+ #
233
+ # Sets the tracing of the logger. Acceptable values are +true+,
234
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
235
+ # change the tracing.
236
+ #
237
+ def trace=( val )
238
+ @trace = case val
239
+ when true, 'true'; true
240
+ when false, 'false'; false
241
+ when nil; @trace
242
+ else raise ArgumentError, 'expecting a boolean' end
243
+ end
244
+
245
+ # call-seq:
246
+ # level = :all
247
+ #
248
+ # Set the level for this logger. The level can be either a +String+, a
249
+ # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
250
+ # the case.
251
+ #
252
+ # There are two special levels -- "all" and "off". The former will
253
+ # enable log messages from this logger. The latter will disable all log
254
+ # messages from this logger.
255
+ #
256
+ # Setting the logger level to +nil+ will cause the parent's logger level
257
+ # to be used.
258
+ #
259
+ # Example:
260
+ #
261
+ # log.level = :debug
262
+ # log.level = "INFO"
263
+ # log.level = 4
264
+ # log.level = 'off'
265
+ # log.level = :all
266
+ #
267
+ # These prodcue an +ArgumentError+
268
+ #
269
+ # log.level = Object
270
+ # log.level = -1
271
+ # log.level = 1_000_000_000_000
272
+ #
273
+ def level=( level )
274
+ lvl = case level
275
+ when String, Symbol; ::Logging::level_num(level)
276
+ when Fixnum; level
277
+ when nil; @parent.level
278
+ else
279
+ raise ArgumentError,
280
+ "level must be a String, Symbol, or Integer"
281
+ end
282
+ if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
283
+ raise ArgumentError, "unknown level was given '#{level}'"
284
+ end
285
+
286
+ @level = lvl
287
+ ::Logging::Logger.define_log_methods(self)
288
+ @level
289
+ end
290
+
291
+ # call-seq:
292
+ # appenders = app
293
+ #
294
+ # Clears the current list of appenders and replaces them with _app_,
295
+ # where _app_ can be either a single appender or an array of appenders.
296
+ #
297
+ def appenders=( args )
298
+ @appenders.clear
299
+ add_appenders(*args) unless args.nil?
300
+ end
301
+
302
+ # call-seq:
303
+ # add_appenders( appenders )
304
+ #
305
+ # Add the given _appenders_ to the list of appenders, where _appenders_
306
+ # can be either a single appender or an array of appenders.
307
+ #
308
+ def add_appenders( *args )
309
+ args.flatten.each do |arg|
310
+ o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appender[arg]
311
+ raise ArgumentError, "unknown appender '#{arg}'" if o.nil?
312
+ @appenders << o unless @appenders.include?(o)
313
+ end
314
+ self
315
+ end
316
+
317
+ # call-seq:
318
+ # remove_appenders( appenders )
319
+ #
320
+ # Remove the given _appenders_ from the list of appenders. The appenders
321
+ # to remove can be identified either by name using a +String+ or by
322
+ # passing the appender instance. _appenders_ can be a single appender or
323
+ # an array of appenders.
324
+ #
325
+ def remove_appenders( *args )
326
+ args.flatten.each do |arg|
327
+ @appenders.delete_if do |a|
328
+ case arg
329
+ when String; arg == a.name
330
+ when ::Logging::Appender; arg.object_id == a.object_id
331
+ else
332
+ raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'"
333
+ end
334
+ end
335
+ end
336
+ self
337
+ end
338
+
339
+ # call-seq:
340
+ # clear_appenders
341
+ #
342
+ # Remove all appenders from this logger.
343
+ #
344
+ def clear_appenders( ) @appenders.clear end
345
+
346
+
347
+ protected
348
+
349
+ # call-seq:
350
+ # parent = ParentLogger
351
+ #
352
+ # Set the parent logger for this logger. This method will be invoked by
353
+ # the +Repository+ class when a parent or child is added to the
354
+ # hierarchy.
355
+ #
356
+ def parent=( parent ) @parent = parent end
357
+
358
+ # call-seq:
359
+ # log_event( event )
360
+ #
361
+ # Send the given _event_ to the appenders for logging, and pass the
362
+ # _event_ up to the parent if additive mode is enabled. The log level has
363
+ # already been checked before this method is called.
364
+ #
365
+ def log_event( event )
366
+ @appenders.each {|a| a.append(event)}
367
+ @parent.log_event(event) if @additive
368
+ end
369
+
370
+ # :stopdoc:
371
+
372
+ # call-seq:
373
+ # meta_eval( code )
374
+ #
375
+ # Evaluates the given string of _code_ if the singleton class of this
376
+ # Logger object.
377
+ #
378
+ def meta_eval( code )
379
+ meta = class << self; self end
380
+ meta.class_eval code
381
+ end
382
+ public :meta_eval
383
+ # :startdoc:
384
+
385
+ end # class Logger
386
+ end # module Logging
387
+
388
+ # EOF