ottobar-logging 0.9.5.1

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 (53) hide show
  1. data/History.txt +158 -0
  2. data/README.rdoc +102 -0
  3. data/Rakefile +41 -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 +107 -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 +49 -0
  25. data/lib/logging/logger.rb +491 -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/test/appenders/test_buffered_io.rb +183 -0
  31. data/test/appenders/test_console.rb +66 -0
  32. data/test/appenders/test_email.rb +171 -0
  33. data/test/appenders/test_file.rb +93 -0
  34. data/test/appenders/test_growl.rb +128 -0
  35. data/test/appenders/test_io.rb +142 -0
  36. data/test/appenders/test_rolling_file.rb +207 -0
  37. data/test/appenders/test_syslog.rb +191 -0
  38. data/test/benchmark.rb +87 -0
  39. data/test/config/test_configurator.rb +70 -0
  40. data/test/config/test_yaml_configurator.rb +40 -0
  41. data/test/layouts/test_basic.rb +43 -0
  42. data/test/layouts/test_pattern.rb +177 -0
  43. data/test/setup.rb +69 -0
  44. data/test/test_appender.rb +166 -0
  45. data/test/test_layout.rb +107 -0
  46. data/test/test_log_event.rb +80 -0
  47. data/test/test_logger.rb +734 -0
  48. data/test/test_logging.rb +267 -0
  49. data/test/test_repository.rb +126 -0
  50. data/test/test_root_logger.rb +81 -0
  51. data/test/test_stats.rb +274 -0
  52. data/test/test_utils.rb +114 -0
  53. metadata +152 -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,49 @@
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
+ # :startdoc:
17
+
18
+ # call-seq:
19
+ # LogEvent.new( logger, level, [data], trace )
20
+ #
21
+ # Creates a new log event with the given _logger_ name, numeric _level_,
22
+ # array of _data_ from the user to be logged, and boolean _trace_ flag.
23
+ # If the _trace_ flag is set to +true+ then Kernel::caller will be
24
+ # invoked to get the execution trace of the logging method.
25
+ #
26
+ def initialize( logger, level, data, trace )
27
+ @logger = logger
28
+ @level = level
29
+ @data = data
30
+ @file = @line = @method = ''
31
+
32
+ if trace
33
+ t = Kernel.caller[2]
34
+ return if t.nil?
35
+
36
+ m = CALLER_RGXP.match(t)
37
+ @file = m[1]
38
+ @line = m[2]
39
+ @method = m[3] unless m[3].nil?
40
+ end
41
+ end
42
+
43
+ attr_accessor :logger, :level, :data
44
+ attr_reader :file, :line, :method
45
+
46
+ end # class LogEvent
47
+ end # module Logging
48
+
49
+ # EOF
@@ -0,0 +1,491 @@
1
+ module Logging
2
+
3
+ # The +Logger+ class is the primary interface to the +Logging+ framework.
4
+ # It provides the logging methods that will be called from user methods,
5
+ # and it generates logging events that are sent to the appenders (the
6
+ # appenders take care of sending the log events to the logging
7
+ # destinations -- files, sockets, etc).
8
+ #
9
+ # +Logger+ instances are obtained from the +Repository+ and should
10
+ # not be directly created by users.
11
+ #
12
+ # Example:
13
+ #
14
+ # log = Logging::Logger['my logger']
15
+ # log.add_appenders( Logging::Appender.stdout ) # append to STDOUT
16
+ # log.level = :info # log 'info' and above
17
+ #
18
+ # log.info 'starting foo operation'
19
+ # ...
20
+ # log.info 'finishing foo operation'
21
+ # ...
22
+ # log.fatal 'unknown exception', exception
23
+ #
24
+ class Logger
25
+
26
+ @mutex = Mutex.new # :nodoc:
27
+ attr_accessor :sync
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
+ alias :write :<<
174
+
175
+ # call-seq:
176
+ # add( severity, message = nil ) {block}
177
+ #
178
+ # Log a message if the given severity is high enough. This is the generic
179
+ # logging method. Users will be more inclined to use #debug, #info, #warn,
180
+ # #error, and #fatal.
181
+ #
182
+ # <b>Message format</b>: +message+ can be any object, but it has to be
183
+ # converted to a String in order to log it. The Logging::format_as
184
+ # method is used to determine how objects chould be converted to
185
+ # strings. Generally, +inspect+ is used.
186
+ #
187
+ # A special case is an +Exception+ object, which will be printed in
188
+ # detail, including message, class, and backtrace.
189
+ #
190
+ # If a _message_ is not given, then the return value from the block is
191
+ # used as the message to log. This is useful when creating the actual
192
+ # message is an expensive operation. This allows the logger to check the
193
+ # severity against the configured level before actually constructing the
194
+ # message.
195
+ #
196
+ # This method returns +true+ if the message was logged, and +false+ is
197
+ # returned if the message was not logged.
198
+ #
199
+ def add( lvl, data = nil )
200
+ lvl = Integer(lvl)
201
+ return false if lvl < level
202
+
203
+ data = yield if block_given?
204
+ log_event(::Logging::LogEvent.new(@name, lvl, data, @trace))
205
+ true
206
+ end
207
+
208
+ # call-seq:
209
+ # additive = true
210
+ #
211
+ # Sets the additivity of the logger. Acceptable values are +true+,
212
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
213
+ # change the additivity
214
+ #
215
+ def additive=( val )
216
+ @additive = case val
217
+ when true, 'true'; true
218
+ when false, 'false'; false
219
+ when nil; @additive
220
+ else raise ArgumentError, 'expecting a boolean' end
221
+ end
222
+
223
+ # call-seq:
224
+ # trace = true
225
+ #
226
+ # Sets the tracing of the logger. Acceptable values are +true+,
227
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
228
+ # change the tracing.
229
+ #
230
+ def trace=( val )
231
+ @trace = case val
232
+ when true, 'true'; true
233
+ when false, 'false'; false
234
+ when nil; @trace
235
+ else raise ArgumentError, 'expecting a boolean' end
236
+ end
237
+
238
+ # call-seq:
239
+ # level => integer
240
+ #
241
+ # Returns an integer which is the defined log level for this logger.
242
+ #
243
+ def level
244
+ return @level unless @level.nil?
245
+ @parent.level
246
+ end
247
+
248
+ # call-seq:
249
+ # level = :all
250
+ #
251
+ # Set the level for this logger. The level can be either a +String+, a
252
+ # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
253
+ # the case.
254
+ #
255
+ # There are two special levels -- "all" and "off". The former will
256
+ # enable log messages from this logger. The latter will disable all log
257
+ # messages from this logger.
258
+ #
259
+ # Setting the logger level to +nil+ will cause the parent's logger level
260
+ # to be used.
261
+ #
262
+ # Example:
263
+ #
264
+ # log.level = :debug
265
+ # log.level = "INFO"
266
+ # log.level = 4
267
+ # log.level = 'off'
268
+ # log.level = :all
269
+ #
270
+ # These prodcue an +ArgumentError+
271
+ #
272
+ # log.level = Object
273
+ # log.level = -1
274
+ # log.level = 1_000_000_000_000
275
+ #
276
+ def level=( level )
277
+ @level =
278
+ if level.nil? then level
279
+ else
280
+ lvl = case level
281
+ when String, Symbol; ::Logging::level_num(level)
282
+ when Fixnum; level
283
+ else
284
+ raise ArgumentError,
285
+ "level must be a String, Symbol, or Integer"
286
+ end
287
+ if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
288
+ raise ArgumentError, "unknown level was given '#{level}'"
289
+ end
290
+ lvl
291
+ end
292
+
293
+ define_log_methods(true)
294
+ self.level
295
+ end
296
+
297
+ # call-seq:
298
+ # appenders = app
299
+ #
300
+ # Clears the current list of appenders and replaces them with _app_,
301
+ # where _app_ can be either a single appender or an array of appenders.
302
+ #
303
+ def appenders=( args )
304
+ @appenders.clear
305
+ add_appenders(*args) unless args.nil?
306
+ end
307
+
308
+ # call-seq:
309
+ # add_appenders( appenders )
310
+ #
311
+ # Add the given _appenders_ to the list of appenders, where _appenders_
312
+ # can be either a single appender or an array of appenders.
313
+ #
314
+ def add_appenders( *args )
315
+ args.flatten.each do |arg|
316
+ o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appender[arg]
317
+ raise ArgumentError, "unknown appender '#{arg}'" if o.nil?
318
+ @appenders << o unless @appenders.include?(o)
319
+ end
320
+ self
321
+ end
322
+
323
+ # call-seq:
324
+ # remove_appenders( appenders )
325
+ #
326
+ # Remove the given _appenders_ from the list of appenders. The appenders
327
+ # to remove can be identified either by name using a +String+ or by
328
+ # passing the appender instance. _appenders_ can be a single appender or
329
+ # an array of appenders.
330
+ #
331
+ def remove_appenders( *args )
332
+ args.flatten.each do |arg|
333
+ @appenders.delete_if do |a|
334
+ case arg
335
+ when String; arg == a.name
336
+ when ::Logging::Appender; arg.object_id == a.object_id
337
+ else
338
+ raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'"
339
+ end
340
+ end
341
+ end
342
+ self
343
+ end
344
+
345
+ # call-seq:
346
+ # clear_appenders
347
+ #
348
+ # Remove all appenders from this logger.
349
+ #
350
+ def clear_appenders( ) @appenders.clear end
351
+
352
+ # call-seq:
353
+ # inspect => string
354
+ #
355
+ # Returns a string representation of the logger.
356
+ #
357
+ def inspect
358
+ "<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name]
359
+ end
360
+
361
+
362
+ protected
363
+
364
+ # call-seq:
365
+ # parent = ParentLogger
366
+ #
367
+ # Set the parent logger for this logger. This method will be invoked by
368
+ # the +Repository+ class when a parent or child is added to the
369
+ # hierarchy.
370
+ #
371
+ def parent=( parent ) @parent = parent end
372
+
373
+ # call-seq:
374
+ # log_event( event )
375
+ #
376
+ # Send the given _event_ to the appenders for logging, and pass the
377
+ # _event_ up to the parent if additive mode is enabled. The log level has
378
+ # already been checked before this method is called.
379
+ #
380
+ def log_event( event )
381
+ @appenders.each {|a| a.append(event)}
382
+ @parent.log_event(event) if @additive
383
+ end
384
+
385
+ # call-seq:
386
+ # define_log_methods( force = false )
387
+ #
388
+ # Define the logging methods for this logger based on the configured log
389
+ # level. If the level is nil, then we will ask our parent for it's level
390
+ # and define log levels accordingly. The force flag will skip this
391
+ # check.
392
+ #
393
+ # Recursively call this method on all our children loggers.
394
+ #
395
+ def define_log_methods( force = false )
396
+ return if @level and !force
397
+
398
+ ::Logging::Logger.define_log_methods(self)
399
+ ::Logging::Repository.instance.children(name).each do |c|
400
+ c.define_log_methods
401
+ end
402
+ self
403
+ end
404
+
405
+ # :stopdoc:
406
+ public
407
+
408
+ # call-seq:
409
+ # _meta_eval( code )
410
+ #
411
+ # Evaluates the given string of _code_ if the singleton class of this
412
+ # Logger object.
413
+ #
414
+ def _meta_eval( code, file = nil, line = nil )
415
+ meta = class << self; self end
416
+ meta.class_eval code, file, line
417
+ end
418
+
419
+ # call-seq:
420
+ # _setup( name, opts = {} )
421
+ #
422
+ # Configures internal variables for the logger. This method can be used
423
+ # to avoid storing the logger in the repository.
424
+ #
425
+ def _setup( name, opts = {} )
426
+ @name = name
427
+ @parent = opts.getopt(:parent)
428
+ @appenders = opts.getopt(:appenders, [])
429
+ @additive = opts.getopt(:additive, true)
430
+ @trace = opts.getopt(:trace, false)
431
+ @level = opts.getopt(:level)
432
+ ::Logging::Logger.define_log_methods(self)
433
+ end
434
+
435
+ # call-seq:
436
+ # _dump_configuration( io = STDOUT, indent = 0 )
437
+ #
438
+ # An internal method that is used to dump this logger's configuration to
439
+ # the given _io_ stream. The configuration includes the logger's name,
440
+ # level, additivity, and trace settings. The configured appenders are
441
+ # also printed to the _io_ stream.
442
+ #
443
+ def _dump_configuration( io = STDOUT, indent = 0 )
444
+ str, spacer, base = '', ' ', 50
445
+ indent_str = indent == 0 ? '' : ' ' * indent
446
+
447
+ str << indent_str
448
+ str << self.name.reduce(base - indent)
449
+ if (str.length + spacer.length) < base
450
+ str << spacer
451
+ str << '.' * (base - str.length)
452
+ end
453
+ io.write(str.ljust(base))
454
+ io.write(spacer)
455
+
456
+ level_str = @level.nil? ? '' : '*'
457
+ level_str << if level < ::Logging::LEVELS.length
458
+ ::Logging.levelify(::Logging::LNAMES[level])
459
+ else
460
+ 'off'
461
+ end
462
+ level_len = ::Logging::MAX_LEVEL_LENGTH + 1
463
+
464
+ io.write("%#{level_len}s" % level_str)
465
+ io.write(spacer)
466
+
467
+ if self.respond_to?(:additive)
468
+ io.write(additive ? '+A' : '-A')
469
+ else
470
+ io.write(' ')
471
+ end
472
+
473
+ io.write(spacer)
474
+ io.write(trace ? '+T' : '-T')
475
+ io.write("\n")
476
+
477
+ @appenders.each do |appender|
478
+ io.write(indent_str)
479
+ io.write('- ')
480
+ io.write(appender.inspect)
481
+ io.write("\n")
482
+ end
483
+
484
+ return io
485
+ end
486
+ # :startdoc:
487
+
488
+ end # class Logger
489
+ end # module Logging
490
+
491
+ # EOF