logging 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.9.5 / 2009-01-25
2
+
3
+ 2 minor enhancements
4
+ - The pattern layout can output the current thread name
5
+ if set using Thread.current[:name] [valodzka]
6
+ - Added buffered logging to all IO based loggers
7
+ (console, file, rolling file)
8
+ 1 bug fix
9
+ - Uncaught TimeoutError in the e-mail appender
10
+
1
11
  == 0.9.4 / 2008-10-04
2
12
 
3
13
  2 minor enhancements
data/README.rdoc CHANGED
@@ -89,6 +89,14 @@ class names.
89
89
  Logging requires the "lockfile" gem to run and the "flexmock" gem to run the
90
90
  tests"
91
91
 
92
+ == DEVELOPMENT REQUIREMENTS
93
+
94
+ The Logging source code relies on the Mr Bones project for default rake tasks.
95
+ You will need to install the Mr Bones gem if you want to build or test the
96
+ logging gem.
97
+
98
+ gem install bones
99
+
92
100
  == LICENSE
93
101
 
94
102
  Ruby
data/Rakefile CHANGED
@@ -1,5 +1,14 @@
1
1
 
2
- load 'tasks/setup.rb'
2
+ begin
3
+ require 'bones'
4
+ Bones.setup
5
+ rescue LoadError
6
+ begin
7
+ load 'tasks/setup.rb'
8
+ rescue LoadError
9
+ raise RuntimeError, '### please install the "bones" gem ###'
10
+ end
11
+ end
3
12
 
4
13
  ensure_in_path 'lib'
5
14
  require 'logging'
@@ -14,11 +23,10 @@ PROJ.url = 'http://logging.rubyforge.org/'
14
23
  PROJ.rubyforge.name = 'logging'
15
24
  PROJ.version = Logging::VERSION
16
25
  PROJ.readme_file = 'README.rdoc'
26
+ PROJ.ignore_file = '.gitignore'
17
27
 
18
- PROJ.exclude << %w[^tags$ ^tasks/archive ^coverage]
28
+ PROJ.exclude << %w[^tags$]
19
29
  PROJ.rdoc.exclude << '^data'
20
- PROJ.rdoc.include << PROJ.readme_file
21
- PROJ.rdoc.main = PROJ.readme_file
22
30
  #PROJ.rdoc.dir = 'doc/rdoc'
23
31
  #PROJ.rdoc.remote_dir = 'rdoc'
24
32
  PROJ.rdoc.dir = 'doc'
data/lib/logging.rb CHANGED
@@ -8,12 +8,16 @@ begin require 'fastthread'; rescue LoadError; end
8
8
 
9
9
  # TODO: Windows Log Service appender
10
10
 
11
+ # TODO: Option to buffer log messages at the appender level
12
+ # extend the concept found in the e-mail appender into the other IO
13
+ # appenders
14
+
11
15
  #
12
16
  #
13
17
  module Logging
14
18
 
15
19
  # :stopdoc:
16
- VERSION = '0.9.4'
20
+ VERSION = '0.9.5'
17
21
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
18
22
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
19
23
  WIN32 = %r/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
@@ -219,6 +219,9 @@ class Appender
219
219
  return self if @closed
220
220
  ::Logging::Appender.remove(@name)
221
221
  @closed = true
222
+
223
+ sync {flush}
224
+
222
225
  if footer
223
226
  footer = @layout.footer
224
227
  unless footer.nil? || footer.empty?
@@ -0,0 +1,167 @@
1
+
2
+ module Logging::Appenders
3
+
4
+ # The Buffering module is used to implement buffering of the log messages
5
+ # in a given appender. The size of the buffer can be specified, and the
6
+ # buffer can be configured to auto-flush at a given threshold. The
7
+ # threshold can be a single message or a very large number of messages.
8
+ #
9
+ # Log messages of a certain level can cause the buffer to be flushed
10
+ # immediately. If an error occurs, all previous messages and the error
11
+ # message will be written immediately to the logging destination if the
12
+ # buffer is configured to do so.
13
+ #
14
+ module Buffering
15
+
16
+ # Default buffer size
17
+ #
18
+ DEFAULT_BUFFER_SIZE = 500;
19
+
20
+ # The buffer holding the log messages
21
+ #
22
+ attr_reader :buffer
23
+
24
+ # The auto-flushing setting. When the buffer reaches this size, all
25
+ # messages will be be flushed automatically.
26
+ #
27
+ attr_reader :auto_flushing
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.
32
+ #
33
+ def flush
34
+ raise NotImplementedError
35
+ end
36
+
37
+ # Configure the levels that will trigger and immediate flush of the
38
+ # logging buffer. When a log event of the given level is seen, the
39
+ # buffer will be flushed immediately. Only the levels explicitly given
40
+ # in this assignment will flush the buffer; if an "error" message is
41
+ # configured to immediately flush the buffer, a "fatal" message will not
42
+ # even though it is a higher level. Both must be explicitly passed to
43
+ # this assignment.
44
+ #
45
+ # You can pass in a single leveal name or number, and array of level
46
+ # names or numbers, or a string containg a comma separated list of level
47
+ # names or numbers.
48
+ #
49
+ # immediate_at = :error
50
+ # immediate_at = [:error, :fatal]
51
+ # immediate_at = "warn, error"
52
+ #
53
+ def immediate_at=( level )
54
+ @immediate ||= []
55
+ @immediate.clear
56
+
57
+ # get the immediate levels -- no buffering occurs at these levels, and
58
+ # a log message is written to the logging destination immediately
59
+ immediate_at =
60
+ case level
61
+ when String; level.split(',').map {|x| x.strip}
62
+ when Array; level
63
+ else Array(level) end
64
+
65
+ immediate_at.each do |lvl|
66
+ num = ::Logging.level_num(lvl)
67
+ next if num.nil?
68
+ @immediate[num] = true
69
+ end
70
+ end
71
+
72
+ # Configure the auto-flushing period. Auto-flushing is used to flush the
73
+ # contents of the logging buffer to the logging destination
74
+ # automatically when the buffer reaches a certain threshold.
75
+ #
76
+ # By default, the auto-flushing will be configured to flush after each
77
+ # log message.
78
+ #
79
+ # The allowed settings are as follows:
80
+ #
81
+ # N : flush after every N messages (N is an integer)
82
+ # true : flush after each log message
83
+ # false OR
84
+ # nil OR
85
+ # 0 : only flush when the buffer is full (500 messages)
86
+ #
87
+ # If the default buffer size of 500 is too small, you can manuall
88
+ # configure to be as large as you want. This will consume more memory.
89
+ #
90
+ # auto_flushing = 42_000
91
+ #
92
+ def auto_flushing=( period )
93
+ @auto_flushing =
94
+ case period
95
+ when true; 1
96
+ when false, nil, 0; DEFAULT_BUFFER_SIZE
97
+ when Integer; period
98
+ when String; Integer(period)
99
+ else
100
+ raise ArgumentError,
101
+ "unrecognized auto_flushing period: #{period.inspect}"
102
+ end
103
+
104
+ if @auto_flushing < 0
105
+ raise ArgumentError,
106
+ "auto_flushing period cannot be negative: #{period.inspect}"
107
+ end
108
+ end
109
+
110
+
111
+ protected
112
+
113
+ # Configure the buffering using the arguments found in the give options
114
+ # hash. This method must be called in order to use the message buffer.
115
+ # The supported options are "immediate_at" and "auto_flushing". Please
116
+ # refer to the documentation for those methods to see the allowed
117
+ # options.
118
+ #
119
+ def configure_buffering( opts )
120
+ @buffer = []
121
+
122
+ self.immediate_at = opts.getopt(:immediate_at, '')
123
+ self.auto_flushing = opts.getopt(:auto_flushing, true)
124
+ end
125
+
126
+ # Append the given event to the message buffer. The event can be either
127
+ # a string or a LogEvent object.
128
+ #
129
+ def add_to_buffer( event )
130
+ str = event.instance_of?(::Logging::LogEvent) ?
131
+ layout.format(event) : event.to_s
132
+ return if str.empty?
133
+
134
+ buffer << str
135
+ flush if buffer.length >= auto_flushing || immediate?(event)
136
+
137
+ self
138
+ end
139
+
140
+ # Returns true if the _event_ level matches one of the configured
141
+ # immediate logging levels. Otherwise returns false.
142
+ #
143
+ def immediate?( event )
144
+ return false unless event.respond_to? :level
145
+ @immediate[event.level]
146
+ end
147
+
148
+
149
+ private
150
+
151
+ # call-seq:
152
+ # write( event )
153
+ #
154
+ # Writes the given _event_ to the logging destination. The _event_ can
155
+ # be either a LogEvent or a String. If a LogEvent, then it will be
156
+ # formatted using the layout given to the appender when it was created.
157
+ #
158
+ def write( event )
159
+ add_to_buffer event
160
+ self
161
+ end
162
+
163
+
164
+ end # module Buffering
165
+ end # module Logging::Appenders
166
+
167
+ # EOF
@@ -10,23 +10,20 @@ require 'time' # get rfc822 time format
10
10
  module Logging::Appenders
11
11
 
12
12
  class Email < ::Logging::Appender
13
+ include Buffering
13
14
 
14
15
  attr_reader :server, :port, :domain, :acct, :authtype, :subject
15
16
 
17
+ # TODO: make the from/to fields modifiable
18
+ # possibly the subject, too
19
+
16
20
  def initialize( name, opts = {} )
17
21
  super(name, opts)
18
22
 
19
- @buff = []
20
- @buffsize = opts.getopt :buffsize, 100, :as => Integer
21
-
22
- # get the immediate levels -- no buffering occurs at these levels, and
23
- # an e-mail is sent as soon as possible
24
- @immediate = []
25
- opts.getopt(:immediate_at, '').split(',').each do |lvl|
26
- num = ::Logging.level_num(lvl.strip)
27
- next if num.nil?
28
- @immediate[num] = true
29
- end
23
+ af = opts.getopt(:buffsize) ||
24
+ opts.getopt(:buffer_size) ||
25
+ 100
26
+ configure_buffering({:auto_flushing => af}.merge(opts))
30
27
 
31
28
  # get the SMTP parameters
32
29
  @from = opts.getopt(:from)
@@ -51,58 +48,7 @@ class Email < ::Logging::Appender
51
48
  # Create and send an email containing the current message buffer.
52
49
  #
53
50
  def flush
54
- sync { send_mail }
55
- self
56
- end
57
-
58
- # call-seq:
59
- # close( footer = true )
60
- #
61
- # Close the e-mail appender and then flush the message buffer. This will
62
- # ensure that a final e-mail is sent with any remaining messages.
63
- #
64
- def close( footer = true )
65
- super
66
- flush
67
- end
68
-
69
- # cal-seq:
70
- # queued_messages => integer
71
- #
72
- # Returns the number of messages in the buffer.
73
- #
74
- def queued_messages
75
- @buff.length
76
- end
77
-
78
-
79
- private
80
-
81
- # call-seq:
82
- # write( event )
83
- #
84
- # Write the given _event_ to the e-mail message buffer. The log event will
85
- # be processed through the Layout associated with this appender.
86
- #
87
- def write( event )
88
- immediate = false
89
- str = if event.instance_of?(::Logging::LogEvent)
90
- immediate = @immediate[event.level]
91
- @layout.format(event)
92
- else
93
- event.to_s
94
- end
95
- return if str.empty?
96
-
97
- @buff << str
98
- send_mail if @buff.length >= @buffsize || immediate
99
- self
100
- end
101
-
102
- # Connect to the mail server and send out any buffered messages.
103
- #
104
- def send_mail
105
- return if @buff.empty?
51
+ return self if buffer.empty?
106
52
 
107
53
  ### build a mail header for RFC 822
108
54
  rfc822msg = "From: #{@from}\n"
@@ -110,18 +56,17 @@ class Email < ::Logging::Appender
110
56
  rfc822msg << "Subject: #{@subject}\n"
111
57
  rfc822msg << "Date: #{Time.new.rfc822}\n"
112
58
  rfc822msg << "Message-Id: <#{"%.8f" % Time.now.to_f}@#{@domain}>\n\n"
113
- rfc822msg << @buff.join
59
+ rfc822msg << buffer.join
114
60
 
115
61
  ### send email
116
- begin
117
- Net::SMTP.start(*@params) {|smtp| smtp.sendmail(rfc822msg, @from, @to)}
118
- rescue StandardError => err
119
- self.level = :off
120
- ::Logging.log_internal {'e-mail notifications have been disabled'}
121
- ::Logging.log_internal(-2) {err}
122
- ensure
123
- @buff.clear
124
- end
62
+ Net::SMTP.start(*@params) {|smtp| smtp.sendmail(rfc822msg, @from, @to)}
63
+ self
64
+ rescue StandardError, TimeoutError => err
65
+ self.level = :off
66
+ ::Logging.log_internal {'e-mail notifications have been disabled'}
67
+ ::Logging.log_internal(-2) {err}
68
+ ensure
69
+ buffer.clear
125
70
  end
126
71
 
127
72
  end # class Email
@@ -5,6 +5,7 @@ module Logging::Appenders
5
5
  # configured for writing.
6
6
  #
7
7
  class IO < ::Logging::Appender
8
+ include Buffering
8
9
 
9
10
  # call-seq:
10
11
  # IO.new( name, io )
@@ -14,12 +15,14 @@ module Logging::Appenders
14
15
  # stream as the logging destination.
15
16
  #
16
17
  def initialize( name, io, opts = {} )
17
- unless io.respond_to? :print
18
+ unless io.respond_to? :write
18
19
  raise TypeError, "expecting an IO object but got '#{io.class.name}'"
19
20
  end
20
21
 
21
22
  @io = io
22
- @io.sync = true if @io.respond_to?('sync') rescue nil
23
+ @io.sync = true if @io.respond_to?(:sync) rescue nil
24
+
25
+ configure_buffering(opts)
23
26
  super(name, opts)
24
27
  end
25
28
 
@@ -33,7 +36,7 @@ module Logging::Appenders
33
36
  #
34
37
  def close( *args )
35
38
  return self if @io.nil?
36
- super(*args)
39
+ super
37
40
  io, @io = @io, nil
38
41
  io.close unless [STDIN, STDERR, STDOUT].include?(io)
39
42
  rescue IOError => err
@@ -49,29 +52,15 @@ module Logging::Appenders
49
52
  #
50
53
  def flush
51
54
  return self if @io.nil?
55
+ @io.write(buffer.join) unless buffer.empty?
52
56
  @io.flush
53
57
  self
54
- end
55
-
56
-
57
- private
58
-
59
- # call-seq:
60
- # write( event )
61
- #
62
- # Writes the given _event_ to the IO stream. If an +IOError+ is detected,
63
- # than this appender will be turned off and the error reported.
64
- #
65
- def write( event )
66
- begin
67
- str = event.instance_of?(::Logging::LogEvent) ?
68
- @layout.format(event) : event.to_s
69
- return if str.empty?
70
- @io.print str
71
- rescue IOError
72
- self.level = :off
73
- raise
74
- end
58
+ rescue StandardError => err
59
+ self.level = :off
60
+ ::Logging.log_internal {"appender #{name.inspect} has been disabled"}
61
+ ::Logging.log_internal(-2) {err}
62
+ ensure
63
+ buffer.clear
75
64
  end
76
65
 
77
66
  end # class IO