logging 1.4.3 → 1.5.0

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 (50) hide show
  1. data/History.txt +8 -0
  2. data/Rakefile +2 -4
  3. data/examples/colorization.rb +62 -0
  4. data/lib/logging.rb +22 -8
  5. data/lib/logging/appender.rb +7 -7
  6. data/lib/logging/appenders/buffering.rb +30 -24
  7. data/lib/logging/appenders/email.rb +7 -11
  8. data/lib/logging/appenders/file.rb +0 -1
  9. data/lib/logging/appenders/io.rb +8 -14
  10. data/lib/logging/appenders/rolling_file.rb +9 -13
  11. data/lib/logging/appenders/string_io.rb +0 -1
  12. data/lib/logging/appenders/syslog.rb +1 -1
  13. data/lib/logging/color_scheme.rb +249 -0
  14. data/lib/logging/layouts/basic.rb +2 -3
  15. data/lib/logging/layouts/parseable.rb +5 -7
  16. data/lib/logging/layouts/pattern.rb +77 -18
  17. data/lib/logging/log_event.rb +1 -1
  18. data/lib/logging/logger.rb +3 -4
  19. data/lib/logging/proxy.rb +57 -0
  20. data/lib/logging/utils.rb +3 -2
  21. data/test/appenders/test_buffered_io.rb +2 -7
  22. data/test/appenders/test_console.rb +1 -1
  23. data/test/appenders/test_email.rb +1 -1
  24. data/test/appenders/test_file.rb +1 -1
  25. data/test/appenders/test_growl.rb +1 -1
  26. data/test/appenders/test_io.rb +4 -5
  27. data/test/appenders/test_rolling_file.rb +1 -1
  28. data/test/appenders/test_syslog.rb +1 -1
  29. data/test/benchmark.rb +15 -11
  30. data/test/config/test_configurator.rb +1 -1
  31. data/test/config/test_yaml_configurator.rb +1 -1
  32. data/test/layouts/test_basic.rb +1 -1
  33. data/test/layouts/test_color_pattern.rb +90 -0
  34. data/test/layouts/test_json.rb +1 -1
  35. data/test/layouts/test_pattern.rb +4 -5
  36. data/test/layouts/test_yaml.rb +1 -1
  37. data/test/test_appender.rb +1 -1
  38. data/test/test_color_scheme.rb +39 -0
  39. data/test/test_consolidate.rb +1 -1
  40. data/test/test_layout.rb +1 -1
  41. data/test/test_log_event.rb +1 -1
  42. data/test/test_logger.rb +1 -1
  43. data/test/test_logging.rb +1 -1
  44. data/test/test_proxy.rb +67 -0
  45. data/test/test_repository.rb +2 -2
  46. data/test/test_root_logger.rb +1 -1
  47. data/test/test_stats.rb +1 -1
  48. data/test/test_utils.rb +1 -1
  49. data/version.txt +1 -1
  50. metadata +40 -17
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 1.5.0 / 2011-03-22
2
+
3
+ Minor Enhancements
4
+ - removed mutexes in favor of IO#syswrite
5
+ - no round tripping through the buffer array when auto_flushing is true
6
+ - added a Proxy object that will log all methods called on it
7
+ - colorization of log messages
8
+
1
9
  == 1.4.3 / 2010-05-31
2
10
 
3
11
  Bug Fixes
data/Rakefile CHANGED
@@ -14,12 +14,10 @@ Bones {
14
14
  authors 'Tim Pease'
15
15
  email 'tim.pease@gmail.com'
16
16
  url 'http://rubygems.org/gems/logging'
17
- readme_file 'README.rdoc'
18
- ignore_file '.gitignore'
19
17
 
20
18
  rdoc.exclude << '^data'
21
19
  rdoc.include << '^examples/.*\.rb'
22
- #rdoc.dir = 'doc/rdoc'
20
+ rcov.opts << '-x' << '~/.rvm/'
23
21
 
24
22
  use_gmail
25
23
 
@@ -27,6 +25,6 @@ Bones {
27
25
 
28
26
  depend_on 'flexmock', :development => true
29
27
  depend_on 'bones-git', :development => true
30
- depend_on 'bones-extras', :development => true
28
+ depend_on 'bones-rcov', :development => true
31
29
  }
32
30
 
@@ -0,0 +1,62 @@
1
+ # :stopdoc:
2
+ #
3
+ # The Pattern layout can colorize log events based on a provided color scheme.
4
+ # The configuration is a two part process. First the color scheme is defined
5
+ # with the level colors and any pattern token colors. This color scheme is
6
+ # then passed by name to the Pattern layout when it is created.
7
+ #
8
+ # The color scheme defines colors to be applied to the level token found in
9
+ # the pattern layout. So that the "info" level will have one color, and the
10
+ # "fatal" level will have a separate color. This applies only to the level
11
+ # token in the Pattern layout.
12
+ #
13
+ # Common tokens can have their own color, too. The date token can be colored
14
+ # blue, and the message token can be colored magenta.
15
+ #
16
+ # Colorization should only be applied to TTY logging destinations like STDOUT
17
+ # and STDERR. Inserting color codes into a log file is generally considered
18
+ # bad from; these color codes cause issues for some command lien programs like
19
+ # "less" and "more".
20
+ #
21
+ # A 'default" color scheme is provided with the Logging framework. In the
22
+ # example below we create our own color scheme called 'bright' and apply it to
23
+ # the 'stdout' appender.
24
+ #
25
+
26
+ require 'logging'
27
+
28
+ # here we setup a color scheme called 'bright'
29
+ Logging.color_scheme( 'bright',
30
+ :levels => {
31
+ :info => :green,
32
+ :warn => :yellow,
33
+ :error => :red,
34
+ :fatal => [:white, :on_red]
35
+ },
36
+ :date => :blue,
37
+ :logger => :cyan,
38
+ :message => :magenta
39
+ )
40
+
41
+ Logging.appenders.stdout(
42
+ 'stdout',
43
+ :layout => Logging.layouts.pattern(
44
+ :pattern => '[%d] %-5l %c: %m\n',
45
+ :color_scheme => 'bright'
46
+ )
47
+ )
48
+
49
+ log = Logging.logger['Happy::Colors']
50
+ log.add_appenders 'stdout'
51
+ log.level = :debug
52
+
53
+ # these log messages will be nicely colored
54
+ # the level will be colored differently for each message
55
+ #
56
+ log.debug "a very nice little debug message"
57
+ log.info "things are operating nominally"
58
+ log.warn "this is your last warning"
59
+ log.error StandardError.new("something went horribly wrong")
60
+ log.fatal "I Die!"
61
+
62
+ # :startdoc:
data/lib/logging.rb CHANGED
@@ -3,12 +3,10 @@
3
3
  # Used to prevent the class/module from being loaded more than once
4
4
  unless defined? Logging
5
5
 
6
- require File.expand_path(
7
- File.join(File.dirname(__FILE__), %w[logging utils]))
6
+ require File.expand_path('../logging/utils', __FILE__)
8
7
 
9
8
  require 'yaml'
10
9
  require 'stringio'
11
- require 'thread'
12
10
  require 'fileutils'
13
11
  require 'little-plugger'
14
12
 
@@ -20,8 +18,8 @@ module Logging
20
18
  extend LittlePlugger
21
19
 
22
20
  # :stopdoc:
23
- LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
24
- PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
21
+ LIBPATH = ::File.expand_path('..', __FILE__) + ::File::SEPARATOR
22
+ PATH = ::File.expand_path('../..', __FILE__) + ::File::SEPARATOR
25
23
  LEVELS = {}
26
24
  LNAMES = []
27
25
  module Plugins; end
@@ -161,6 +159,21 @@ module Logging
161
159
  ::Logging::Appenders
162
160
  end
163
161
 
162
+ # Returns the color scheme identified by the given _name_. If there is no
163
+ # color scheme +nil+ is returned.
164
+ #
165
+ # If color scheme options are supplied then a new color scheme is created.
166
+ # Any existing color scheme with the given _name_ will be replaced by the
167
+ # new color scheme.
168
+ #
169
+ def color_scheme( name, opts = {} )
170
+ if opts.empty?
171
+ ::Logging::ColorScheme[name]
172
+ else
173
+ ::Logging::ColorScheme.new(name, opts)
174
+ end
175
+ end
176
+
164
177
  # Reopen all appenders. This method should be called immediately after a
165
178
  # fork to ensure no conflict with file descriptors and calls to fcntl or
166
179
  # flock.
@@ -290,7 +303,6 @@ module Logging
290
303
  module_eval "MAX_LEVEL_LENGTH = #{longest.length}", __FILE__, __LINE__
291
304
 
292
305
  initialize_plugins
293
-
294
306
  levels.keys
295
307
  end
296
308
 
@@ -468,7 +480,7 @@ module Logging
468
480
 
469
481
  # Convert the given level into a level number.
470
482
  def level_num( level )
471
- l = levelify level
483
+ l = levelify(level) rescue level
472
484
  case l
473
485
  when 'all'; 0
474
486
  when 'off'; LEVELS.length
@@ -490,6 +502,7 @@ module Logging
490
502
  def reset
491
503
  ::Logging::Repository.reset
492
504
  ::Logging::Appenders.reset
505
+ ::Logging::ColorScheme.reset
493
506
  LEVELS.clear
494
507
  LNAMES.clear
495
508
  remove_instance_variable :@backtrace if defined? @backtrace
@@ -509,8 +522,10 @@ Logging.libpath {
509
522
  require 'logging/repository'
510
523
  require 'logging/root_logger'
511
524
  require 'logging/stats'
525
+ require 'logging/color_scheme'
512
526
  require 'logging/appenders'
513
527
  require 'logging/layouts'
528
+ require 'logging/proxy'
514
529
 
515
530
  require 'logging/config/configurator'
516
531
  require 'logging/config/yaml_configurator'
@@ -525,4 +540,3 @@ at_exit {Logging.shutdown}
525
540
 
526
541
  end # unless defined?
527
542
 
528
- # EOF
@@ -45,8 +45,8 @@ class Appender
45
45
  header = @layout.header
46
46
 
47
47
  unless header.nil? || header.empty?
48
- begin
49
- sync {write(header)}
48
+ begin
49
+ write(header)
50
50
  rescue StandardError => err
51
51
  ::Logging.log_internal(-2) {err}
52
52
  end
@@ -71,7 +71,7 @@ class Appender
71
71
  # appender level
72
72
  unless @level > event.level
73
73
  begin
74
- sync {write(event)}
74
+ write(event)
75
75
  rescue StandardError => err
76
76
  ::Logging.log_internal(-2) {err}
77
77
  end
@@ -94,7 +94,7 @@ class Appender
94
94
 
95
95
  unless @level >= ::Logging::LEVELS.length
96
96
  begin
97
- sync {write(str)}
97
+ write(str)
98
98
  rescue StandardError => err
99
99
  ::Logging.log_internal(-2) {err}
100
100
  end
@@ -170,13 +170,13 @@ class Appender
170
170
  ::Logging::Appenders.remove(@name)
171
171
  @closed = true
172
172
 
173
- sync {flush}
173
+ flush
174
174
 
175
175
  if footer
176
176
  footer = @layout.footer
177
177
  unless footer.nil? || footer.empty?
178
178
  begin
179
- sync {write(footer)}
179
+ write(footer)
180
180
  rescue StandardError => err
181
181
  ::Logging.log_internal(-2) {err}
182
182
  end
@@ -229,7 +229,7 @@ class Appender
229
229
  end
230
230
 
231
231
 
232
- private
232
+ private
233
233
 
234
234
  # call-seq:
235
235
  # write( event )
@@ -26,12 +26,20 @@ module Logging::Appenders
26
26
  #
27
27
  attr_reader :auto_flushing
28
28
 
29
- # This message must be implemented by the including class. The method
30
- # should write the contents of the buffer to the logging destination,
31
- # and it should clear the buffer when done.
29
+ # Call +flush+ to force an appender to write out any buffered log events.
30
+ # Similar to IO#flush, so use in a similar fashion.
32
31
  #
33
32
  def flush
34
- raise NotImplementedError
33
+ return self if @buffer.empty?
34
+
35
+ str = nil
36
+ sync {
37
+ str = @buffer.join
38
+ @buffer.clear
39
+ }
40
+
41
+ canonical_write str unless str.empty?
42
+ self
35
43
  end
36
44
 
37
45
  # Configure the levels that will trigger and immediate flush of the
@@ -61,7 +69,7 @@ module Logging::Appenders
61
69
  when String; level.split(',').map {|x| x.strip}
62
70
  when Array; level
63
71
  else Array(level) end
64
-
72
+
65
73
  immediate_at.each do |lvl|
66
74
  num = ::Logging.level_num(lvl)
67
75
  next if num.nil?
@@ -108,7 +116,7 @@ module Logging::Appenders
108
116
  end
109
117
 
110
118
 
111
- protected
119
+ protected
112
120
 
113
121
  # Configure the buffering using the arguments found in the give options
114
122
  # hash. This method must be called in order to use the message buffer.
@@ -124,20 +132,6 @@ module Logging::Appenders
124
132
  self.auto_flushing = opts.getopt(:auto_flushing, true)
125
133
  end
126
134
 
127
- # Append the given event to the message buffer. The event can be either
128
- # a string or a LogEvent object.
129
- #
130
- def add_to_buffer( event )
131
- str = event.instance_of?(::Logging::LogEvent) ?
132
- layout.format(event) : event.to_s
133
- return if str.empty?
134
-
135
- buffer << str
136
- flush if buffer.length >= auto_flushing || immediate?(event)
137
-
138
- self
139
- end
140
-
141
135
  # Returns true if the _event_ level matches one of the configured
142
136
  # immediate logging levels. Otherwise returns false.
143
137
  #
@@ -147,7 +141,7 @@ module Logging::Appenders
147
141
  end
148
142
 
149
143
 
150
- private
144
+ private
151
145
 
152
146
  # call-seq:
153
147
  # write( event )
@@ -156,13 +150,25 @@ module Logging::Appenders
156
150
  # be either a LogEvent or a String. If a LogEvent, then it will be
157
151
  # formatted using the layout given to the appender when it was created.
158
152
  #
153
+ # The _event_ will be formatted and then buffered until the
154
+ # "auto_flushing" level has been reached. At thsi time the canonical_write
155
+ # method will be used to log all events stored in the buffer.
156
+ #
159
157
  def write( event )
160
- add_to_buffer event
158
+ str = event.instance_of?(::Logging::LogEvent) ?
159
+ layout.format(event) : event.to_s
160
+ return if str.empty?
161
+
162
+ if @auto_flushing == 1
163
+ canonical_write(str)
164
+ else
165
+ sync { @buffer << str }
166
+ flush if @buffer.length >= @auto_flushing || immediate?(event)
167
+ end
168
+
161
169
  self
162
170
  end
163
171
 
164
-
165
172
  end # module Buffering
166
173
  end # module Logging::Appenders
167
174
 
168
- # EOF
@@ -42,21 +42,20 @@ class Email < ::Logging::Appender
42
42
  @params = [@server, @port, @domain, @acct, @passwd, @authtype]
43
43
  end
44
44
 
45
- # call-seq:
46
- # flush
47
- #
48
- # Create and send an email containing the current message buffer.
49
- #
50
- def flush
51
- return self if buffer.empty?
52
45
 
46
+ private
47
+
48
+ # This method is called by the buffering code when messages need to be
49
+ # sent out as an email.
50
+ #
51
+ def canonical_write( str )
53
52
  ### build a mail header for RFC 822
54
53
  rfc822msg = "From: #{@from}\n"
55
54
  rfc822msg << "To: #{@to.join(",")}\n"
56
55
  rfc822msg << "Subject: #{@subject}\n"
57
56
  rfc822msg << "Date: #{Time.new.rfc822}\n"
58
57
  rfc822msg << "Message-Id: <#{"%.8f" % Time.now.to_f}@#{@domain}>\n\n"
59
- rfc822msg << buffer.join
58
+ rfc822msg << str
60
59
 
61
60
  ### send email
62
61
  Net::SMTP.start(*@params) {|smtp| smtp.sendmail(rfc822msg, @from, @to)}
@@ -65,11 +64,8 @@ class Email < ::Logging::Appender
65
64
  self.level = :off
66
65
  ::Logging.log_internal {'e-mail notifications have been disabled'}
67
66
  ::Logging.log_internal(-2) {err}
68
- ensure
69
- buffer.clear
70
67
  end
71
68
 
72
69
  end # class Email
73
70
  end # module Logging::Appenders
74
71
 
75
- # EOF
@@ -64,7 +64,6 @@ module Logging::Appenders
64
64
  end
65
65
  @closed = false
66
66
  @io = ::File.new(@fn, @mode)
67
- @io.sync = true
68
67
  }
69
68
  self
70
69
  end
@@ -15,13 +15,11 @@ module Logging::Appenders
15
15
  # stream as the logging destination.
16
16
  #
17
17
  def initialize( name, io, opts = {} )
18
- unless io.respond_to? :write
18
+ unless io.respond_to? :syswrite
19
19
  raise TypeError, "expecting an IO object but got '#{io.class.name}'"
20
20
  end
21
21
 
22
22
  @io = io
23
- @io.sync = true if @io.respond_to?(:sync) rescue nil
24
-
25
23
  configure_buffering(opts)
26
24
  super(name, opts)
27
25
  end
@@ -44,26 +42,22 @@ module Logging::Appenders
44
42
  return self
45
43
  end
46
44
 
47
- # call-seq:
48
- # flush
49
- #
50
- # Call +flush+ to force an appender to write out any buffered log events.
51
- # Similar to IO#flush, so use in a similar fashion.
45
+
46
+ private
47
+
48
+ # This method is called by the buffering code when messages need to be
49
+ # written to the logging destination.
52
50
  #
53
- def flush
51
+ def canonical_write( str )
54
52
  return self if @io.nil?
55
- @io.write(buffer.join) unless buffer.empty?
56
- @io.flush
53
+ @io.syswrite str
57
54
  self
58
55
  rescue StandardError => err
59
56
  self.level = :off
60
57
  ::Logging.log_internal {"appender #{name.inspect} has been disabled"}
61
58
  ::Logging.log_internal(-2) {err}
62
- ensure
63
- buffer.clear
64
59
  end
65
60
 
66
61
  end # class IO
67
62
  end # module Logging::Appenders
68
63
 
69
- # EOF
@@ -168,29 +168,21 @@ module Logging::Appenders
168
168
  end
169
169
  @closed = false
170
170
  @io = ::File.new(@fn, 'a')
171
- @io.sync = true
172
171
  }
173
172
  self
174
173
  end
175
174
 
176
175
 
177
- private
176
+ private
178
177
 
179
- alias :_write :write
180
-
181
- # call-seq:
182
- # write( event )
183
- #
184
178
  # Write the given _event_ to the log file. The log file will be rolled
185
179
  # if the maximum file size is exceeded or if the file is older than the
186
180
  # maximum age.
187
181
  #
188
- def write( event )
189
- str = event.instance_of?(::Logging::LogEvent) ?
190
- @layout.format(event) : event.to_s
191
- return if str.empty?
182
+ def canonical_write( str )
183
+ return self if @io.nil?
192
184
 
193
- @io.flock_sh { _write(str) }
185
+ @io.flock_sh { @io.syswrite(str) }
194
186
 
195
187
  if roll_required?
196
188
  @io.flock? {
@@ -199,6 +191,11 @@ module Logging::Appenders
199
191
  }
200
192
  @roller.roll_files
201
193
  end
194
+ self
195
+ rescue StandardError => err
196
+ self.level = :off
197
+ ::Logging.log_internal {"appender #{name.inspect} has been disabled"}
198
+ ::Logging.log_internal(-2) {err}
202
199
  end
203
200
 
204
201
  # Returns +true+ if the log file needs to be rolled.
@@ -326,4 +323,3 @@ module Logging::Appenders
326
323
  end # class RollingFile
327
324
  end # module Logging::Appenders
328
325
 
329
- # EOF