sgeorgi-logging 1.4.2

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 (72) hide show
  1. data/History.txt +262 -0
  2. data/README.rdoc +115 -0
  3. data/Rakefile +32 -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/examples/appenders.rb +47 -0
  10. data/examples/classes.rb +41 -0
  11. data/examples/consolidation.rb +83 -0
  12. data/examples/fork.rb +37 -0
  13. data/examples/formatting.rb +51 -0
  14. data/examples/hierarchies.rb +73 -0
  15. data/examples/layouts.rb +48 -0
  16. data/examples/loggers.rb +29 -0
  17. data/examples/names.rb +43 -0
  18. data/examples/simple.rb +17 -0
  19. data/lib/logging.rb +528 -0
  20. data/lib/logging/appender.rb +260 -0
  21. data/lib/logging/appenders.rb +137 -0
  22. data/lib/logging/appenders/buffering.rb +178 -0
  23. data/lib/logging/appenders/console.rb +60 -0
  24. data/lib/logging/appenders/email.rb +75 -0
  25. data/lib/logging/appenders/file.rb +75 -0
  26. data/lib/logging/appenders/growl.rb +197 -0
  27. data/lib/logging/appenders/io.rb +69 -0
  28. data/lib/logging/appenders/rolling_file.rb +327 -0
  29. data/lib/logging/appenders/string_io.rb +68 -0
  30. data/lib/logging/appenders/syslog.rb +210 -0
  31. data/lib/logging/config/configurator.rb +188 -0
  32. data/lib/logging/config/yaml_configurator.rb +191 -0
  33. data/lib/logging/layout.rb +117 -0
  34. data/lib/logging/layouts.rb +47 -0
  35. data/lib/logging/layouts/basic.rb +32 -0
  36. data/lib/logging/layouts/parseable.rb +211 -0
  37. data/lib/logging/layouts/pattern.rb +311 -0
  38. data/lib/logging/log_event.rb +45 -0
  39. data/lib/logging/logger.rb +504 -0
  40. data/lib/logging/repository.rb +232 -0
  41. data/lib/logging/root_logger.rb +61 -0
  42. data/lib/logging/stats.rb +278 -0
  43. data/lib/logging/utils.rb +201 -0
  44. data/lib/spec/logging_helper.rb +34 -0
  45. data/test/appenders/test_buffered_io.rb +176 -0
  46. data/test/appenders/test_console.rb +66 -0
  47. data/test/appenders/test_email.rb +170 -0
  48. data/test/appenders/test_file.rb +95 -0
  49. data/test/appenders/test_growl.rb +127 -0
  50. data/test/appenders/test_io.rb +129 -0
  51. data/test/appenders/test_rolling_file.rb +209 -0
  52. data/test/appenders/test_syslog.rb +194 -0
  53. data/test/benchmark.rb +86 -0
  54. data/test/config/test_configurator.rb +70 -0
  55. data/test/config/test_yaml_configurator.rb +40 -0
  56. data/test/layouts/test_basic.rb +42 -0
  57. data/test/layouts/test_json.rb +112 -0
  58. data/test/layouts/test_pattern.rb +198 -0
  59. data/test/layouts/test_yaml.rb +121 -0
  60. data/test/setup.rb +43 -0
  61. data/test/test_appender.rb +152 -0
  62. data/test/test_consolidate.rb +46 -0
  63. data/test/test_layout.rb +110 -0
  64. data/test/test_log_event.rb +80 -0
  65. data/test/test_logger.rb +699 -0
  66. data/test/test_logging.rb +267 -0
  67. data/test/test_repository.rb +158 -0
  68. data/test/test_root_logger.rb +81 -0
  69. data/test/test_stats.rb +274 -0
  70. data/test/test_utils.rb +116 -0
  71. data/version.txt +1 -0
  72. metadata +227 -0
@@ -0,0 +1,45 @@
1
+
2
+ module Logging
3
+
4
+ # This class defines a logging event.
5
+ #
6
+ LogEvent = Struct.new( :logger, :level, :data, :time, :file, :line, :method ) {
7
+ # :stopdoc:
8
+
9
+ # Regular expression used to parse out caller information
10
+ #
11
+ # * $1 == filename
12
+ # * $2 == line number
13
+ # * $3 == method name (might be nil)
14
+ CALLER_RGXP = %r/([\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
15
+ CALLER_INDEX = 2
16
+ #CALLER_INDEX = RUBY_PLATFORM[%r/^java/i] ? 1 : 2
17
+ # :startdoc:
18
+
19
+ # call-seq:
20
+ # LogEvent.new( logger, level, [data], trace )
21
+ #
22
+ # Creates a new log event with the given _logger_ name, numeric _level_,
23
+ # array of _data_ from the user to be logged, and boolean _trace_ flag.
24
+ # If the _trace_ flag is set to +true+ then Kernel::caller will be
25
+ # invoked to get the execution trace of the logging method.
26
+ #
27
+ def initialize( logger, level, data, trace )
28
+ f = l = m = ''
29
+
30
+ if trace
31
+ stack = Kernel.caller[CALLER_INDEX]
32
+ return if stack.nil?
33
+
34
+ match = CALLER_RGXP.match(stack)
35
+ f = match[1]
36
+ l = Integer(match[2])
37
+ m = match[3] unless match[3].nil?
38
+ end
39
+
40
+ super(logger, level, data, Time.now, f, l, m)
41
+ end
42
+ }
43
+ end # module Logging
44
+
45
+ # EOF
@@ -0,0 +1,504 @@
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.appenders.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
+
55
+ master = repo.master_for(name)
56
+ if master
57
+ if repo.has_logger?(master)
58
+ logger = repo[master]
59
+ else
60
+ logger = super(master)
61
+ repo[master] = logger
62
+ repo.children(master).each {|c| c.__send__(:parent=, logger)}
63
+ end
64
+ repo[name] = logger
65
+ else
66
+ logger = super(name)
67
+ repo[name] = logger
68
+ repo.children(name).each {|c| c.__send__(:parent=, logger)}
69
+ end
70
+ end
71
+ logger
72
+ end
73
+ end
74
+ alias :[] :new
75
+
76
+ # This is where the actual logging methods are defined. Two methods
77
+ # are created for each log level. The first is a query method used to
78
+ # determine if that perticular logging level is enabled. The second is
79
+ # the actual logging method that accepts a list of objects to be
80
+ # logged or a block. If a block is given, then the object returned
81
+ # from the block will be logged.
82
+ #
83
+ # Example
84
+ #
85
+ # log = Logging::Logger['my logger']
86
+ # log.level = :warn
87
+ #
88
+ # log.info? # => false
89
+ # log.warn? # => true
90
+ # log.warn 'this is your last warning'
91
+ # log.fatal 'I die!', exception
92
+ #
93
+ # log.debug do
94
+ # # expensive method to construct log message
95
+ # msg
96
+ # end
97
+ #
98
+ def define_log_methods( logger )
99
+ ::Logging::LEVELS.each do |name,num|
100
+ code = "undef :#{name} if method_defined? :#{name}\n"
101
+ code << "undef :#{name}? if method_defined? :#{name}?\n"
102
+
103
+ if logger.level > num
104
+ code << <<-CODE
105
+ def #{name}?( ) false end
106
+ def #{name}( data = nil ) false end
107
+ CODE
108
+ else
109
+ code << <<-CODE
110
+ def #{name}?( ) true end
111
+ def #{name}( data = nil )
112
+ data = yield if block_given?
113
+ log_event(::Logging::LogEvent.new(@name, #{num}, data, @trace))
114
+ true
115
+ end
116
+ CODE
117
+ end
118
+
119
+ logger._meta_eval(code, __FILE__, __LINE__)
120
+ end
121
+ logger
122
+ end
123
+ # :startdoc:
124
+
125
+ end # class << self
126
+
127
+ attr_reader :name, :parent, :additive, :trace
128
+
129
+ # call-seq:
130
+ # Logger.new( name )
131
+ # Logger[name]
132
+ #
133
+ # Returns the logger identified by _name_.
134
+ #
135
+ # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to
136
+ # retrieve the logger. When _name_ is a +Class+ the class name will be
137
+ # used to retrieve the logger. When _name_ is an object the name of the
138
+ # object's class will be used to retrieve the logger.
139
+ #
140
+ # Example:
141
+ #
142
+ # obj = MyClass.new
143
+ #
144
+ # log1 = Logger.new(obj)
145
+ # log2 = Logger.new(MyClass)
146
+ # log3 = Logger['MyClass']
147
+ #
148
+ # log1.object_id == log2.object_id # => true
149
+ # log2.object_id == log3.object_id # => true
150
+ #
151
+ def initialize( name )
152
+ case name
153
+ when String
154
+ raise(ArgumentError, "logger must have a name") if name.empty?
155
+ else raise(ArgumentError, "logger name must be a String") end
156
+
157
+ repo = ::Logging::Repository.instance
158
+ _setup(name, :parent => repo.parent(name))
159
+ end
160
+
161
+ # call-seq:
162
+ # log <=> other
163
+ #
164
+ # Compares this logger by name to another logger. The normal return codes
165
+ # for +String+ objects apply.
166
+ #
167
+ def <=>( other )
168
+ case other
169
+ when self; 0
170
+ when ::Logging::RootLogger; 1
171
+ when ::Logging::Logger; @name <=> other.name
172
+ else raise ArgumentError, 'expecting a Logger instance' end
173
+ end
174
+
175
+ # call-seq:
176
+ # log << "message"
177
+ #
178
+ # Log the given message without any formatting and without performing any
179
+ # level checks. The message is logged to all appenders. The message is
180
+ # passed up the logger tree if this logger's additivity is +true+.
181
+ #
182
+ def <<( msg )
183
+ @appenders.each {|a| a << msg}
184
+ @parent << msg if @additive
185
+ end
186
+ alias :write :<<
187
+
188
+ # call-seq:
189
+ # add( severity, message = nil ) {block}
190
+ #
191
+ # Log a message if the given severity is high enough. This is the generic
192
+ # logging method. Users will be more inclined to use #debug, #info, #warn,
193
+ # #error, and #fatal.
194
+ #
195
+ # <b>Message format</b>: +message+ can be any object, but it has to be
196
+ # converted to a String in order to log it. The Logging::format_as
197
+ # method is used to determine how objects chould be converted to
198
+ # strings. Generally, +inspect+ is used.
199
+ #
200
+ # A special case is an +Exception+ object, which will be printed in
201
+ # detail, including message, class, and backtrace.
202
+ #
203
+ # If a _message_ is not given, then the return value from the block is
204
+ # used as the message to log. This is useful when creating the actual
205
+ # message is an expensive operation. This allows the logger to check the
206
+ # severity against the configured level before actually constructing the
207
+ # message.
208
+ #
209
+ # This method returns +true+ if the message was logged, and +false+ is
210
+ # returned if the message was not logged.
211
+ #
212
+ def add( lvl, data = nil )
213
+ lvl = Integer(lvl)
214
+ return false if lvl < level
215
+
216
+ data = yield if block_given?
217
+ log_event(::Logging::LogEvent.new(@name, lvl, data, @trace))
218
+ true
219
+ end
220
+
221
+ # call-seq:
222
+ # additive = true
223
+ #
224
+ # Sets the additivity of the logger. Acceptable values are +true+,
225
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
226
+ # change the additivity
227
+ #
228
+ def additive=( val )
229
+ @additive = case val
230
+ when true, 'true'; true
231
+ when false, 'false'; false
232
+ when nil; @additive
233
+ else raise ArgumentError, 'expecting a boolean' end
234
+ end
235
+
236
+ # call-seq:
237
+ # trace = true
238
+ #
239
+ # Sets the tracing of the logger. Acceptable values are +true+,
240
+ # 'true', +false+, 'false', or +nil+. In this case +nil+ does not
241
+ # change the tracing.
242
+ #
243
+ def trace=( val )
244
+ @trace = case val
245
+ when true, 'true'; true
246
+ when false, 'false'; false
247
+ when nil; @trace
248
+ else raise ArgumentError, 'expecting a boolean' end
249
+ end
250
+
251
+ # call-seq:
252
+ # level => integer
253
+ #
254
+ # Returns an integer which is the defined log level for this logger.
255
+ #
256
+ def level
257
+ return @level unless @level.nil?
258
+ @parent.level
259
+ end
260
+
261
+ # call-seq:
262
+ # level = :all
263
+ #
264
+ # Set the level for this logger. The level can be either a +String+, a
265
+ # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
266
+ # the case.
267
+ #
268
+ # There are two special levels -- "all" and "off". The former will
269
+ # enable log messages from this logger. The latter will disable all log
270
+ # messages from this logger.
271
+ #
272
+ # Setting the logger level to +nil+ will cause the parent's logger level
273
+ # to be used.
274
+ #
275
+ # Example:
276
+ #
277
+ # log.level = :debug
278
+ # log.level = "INFO"
279
+ # log.level = 4
280
+ # log.level = 'off'
281
+ # log.level = :all
282
+ #
283
+ # These prodcue an +ArgumentError+
284
+ #
285
+ # log.level = Object
286
+ # log.level = -1
287
+ # log.level = 1_000_000_000_000
288
+ #
289
+ def level=( level )
290
+ @level =
291
+ if level.nil? then level
292
+ else
293
+ lvl = case level
294
+ when String, Symbol; ::Logging::level_num(level)
295
+ when Fixnum; level
296
+ else
297
+ raise ArgumentError,
298
+ "level must be a String, Symbol, or Integer"
299
+ end
300
+ if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
301
+ raise ArgumentError, "unknown level was given '#{level}'"
302
+ end
303
+ lvl
304
+ end
305
+
306
+ define_log_methods(true)
307
+ self.level
308
+ end
309
+
310
+ # call-seq:
311
+ # appenders = app
312
+ #
313
+ # Clears the current list of appenders and replaces them with _app_,
314
+ # where _app_ can be either a single appender or an array of appenders.
315
+ #
316
+ def appenders=( args )
317
+ @appenders.clear
318
+ add_appenders(*args) unless args.nil?
319
+ end
320
+
321
+ # call-seq:
322
+ # add_appenders( appenders )
323
+ #
324
+ # Add the given _appenders_ to the list of appenders, where _appenders_
325
+ # can be either a single appender or an array of appenders.
326
+ #
327
+ def add_appenders( *args )
328
+ args.flatten.each do |arg|
329
+ o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appenders[arg.to_s]
330
+ raise ArgumentError, "unknown appender #{arg.inspect}" if o.nil?
331
+ @appenders << o unless @appenders.include?(o)
332
+ end
333
+ self
334
+ end
335
+
336
+ # call-seq:
337
+ # remove_appenders( appenders )
338
+ #
339
+ # Remove the given _appenders_ from the list of appenders. The appenders
340
+ # to remove can be identified either by name using a +String+ or by
341
+ # passing the appender instance. _appenders_ can be a single appender or
342
+ # an array of appenders.
343
+ #
344
+ def remove_appenders( *args )
345
+ args.flatten.each do |arg|
346
+ @appenders.delete_if do |a|
347
+ case arg
348
+ when String; arg == a.name
349
+ when ::Logging::Appender; arg.object_id == a.object_id
350
+ else
351
+ raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'"
352
+ end
353
+ end
354
+ end
355
+ self
356
+ end
357
+
358
+ # call-seq:
359
+ # clear_appenders
360
+ #
361
+ # Remove all appenders from this logger.
362
+ #
363
+ def clear_appenders( ) @appenders.clear end
364
+
365
+ # call-seq:
366
+ # inspect => string
367
+ #
368
+ # Returns a string representation of the logger.
369
+ #
370
+ def inspect
371
+ "<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name]
372
+ end
373
+
374
+
375
+ protected
376
+
377
+ # call-seq:
378
+ # parent = ParentLogger
379
+ #
380
+ # Set the parent logger for this logger. This method will be invoked by
381
+ # the +Repository+ class when a parent or child is added to the
382
+ # hierarchy.
383
+ #
384
+ def parent=( parent ) @parent = parent end
385
+
386
+ # call-seq:
387
+ # log_event( event )
388
+ #
389
+ # Send the given _event_ to the appenders for logging, and pass the
390
+ # _event_ up to the parent if additive mode is enabled. The log level has
391
+ # already been checked before this method is called.
392
+ #
393
+ def log_event( event )
394
+ @appenders.each {|a| a.append(event)}
395
+ @parent.log_event(event) if @additive
396
+ end
397
+
398
+ # call-seq:
399
+ # define_log_methods( force = false )
400
+ #
401
+ # Define the logging methods for this logger based on the configured log
402
+ # level. If the level is nil, then we will ask our parent for it's level
403
+ # and define log levels accordingly. The force flag will skip this
404
+ # check.
405
+ #
406
+ # Recursively call this method on all our children loggers.
407
+ #
408
+ def define_log_methods( force = false )
409
+ return if @level and !force
410
+
411
+ ::Logging::Logger.define_log_methods(self)
412
+ ::Logging::Repository.instance.children(name).each do |c|
413
+ c.define_log_methods
414
+ end
415
+ self
416
+ end
417
+
418
+ # :stopdoc:
419
+ public
420
+
421
+ # call-seq:
422
+ # _meta_eval( code )
423
+ #
424
+ # Evaluates the given string of _code_ if the singleton class of this
425
+ # Logger object.
426
+ #
427
+ def _meta_eval( code, file = nil, line = nil )
428
+ meta = class << self; self end
429
+ meta.class_eval code, file, line
430
+ end
431
+
432
+ # call-seq:
433
+ # _setup( name, opts = {} )
434
+ #
435
+ # Configures internal variables for the logger. This method can be used
436
+ # to avoid storing the logger in the repository.
437
+ #
438
+ def _setup( name, opts = {} )
439
+ @name = name
440
+ @parent = opts.getopt(:parent)
441
+ @appenders = opts.getopt(:appenders, [])
442
+ @additive = opts.getopt(:additive, true)
443
+ @trace = opts.getopt(:trace, false)
444
+ @level = opts.getopt(:level)
445
+ ::Logging::Logger.define_log_methods(self)
446
+ end
447
+
448
+ # call-seq:
449
+ # _dump_configuration( io = STDOUT, indent = 0 )
450
+ #
451
+ # An internal method that is used to dump this logger's configuration to
452
+ # the given _io_ stream. The configuration includes the logger's name,
453
+ # level, additivity, and trace settings. The configured appenders are
454
+ # also printed to the _io_ stream.
455
+ #
456
+ def _dump_configuration( io = STDOUT, indent = 0 )
457
+ str, spacer, base = '', ' ', 50
458
+ indent_str = indent == 0 ? '' : ' ' * indent
459
+
460
+ str << indent_str
461
+ str << self.name.reduce(base - indent)
462
+ if (str.length + spacer.length) < base
463
+ str << spacer
464
+ str << '.' * (base - str.length)
465
+ end
466
+ io.write(str.ljust(base))
467
+ io.write(spacer)
468
+
469
+ level_str = @level.nil? ? '' : '*'
470
+ level_str << if level < ::Logging::LEVELS.length
471
+ ::Logging.levelify(::Logging::LNAMES[level])
472
+ else
473
+ 'off'
474
+ end
475
+ level_len = ::Logging::MAX_LEVEL_LENGTH + 1
476
+
477
+ io.write("%#{level_len}s" % level_str)
478
+ io.write(spacer)
479
+
480
+ if self.respond_to?(:additive)
481
+ io.write(additive ? '+A' : '-A')
482
+ else
483
+ io.write(' ')
484
+ end
485
+
486
+ io.write(spacer)
487
+ io.write(trace ? '+T' : '-T')
488
+ io.write("\n")
489
+
490
+ @appenders.each do |appender|
491
+ io.write(indent_str)
492
+ io.write('- ')
493
+ io.write(appender.inspect)
494
+ io.write("\n")
495
+ end
496
+
497
+ return io
498
+ end
499
+ # :startdoc:
500
+
501
+ end # class Logger
502
+ end # module Logging
503
+
504
+ # EOF