TwP-logging 0.9.7

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