sgeorgi-logging 1.4.2

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