TwP-logging 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
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