logging 1.4.3 → 1.5.0

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