logging 0.9.4 → 0.9.5
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.
- data/History.txt +10 -0
- data/README.rdoc +8 -0
- data/Rakefile +12 -4
- data/lib/logging.rb +5 -1
- data/lib/logging/appender.rb +3 -0
- data/lib/logging/appenders/buffering.rb +167 -0
- data/lib/logging/appenders/email.rb +18 -73
- data/lib/logging/appenders/io.rb +13 -24
- data/lib/logging/layout.rb +0 -3
- data/lib/logging/layouts/pattern.rb +5 -0
- data/lib/logging/logger.rb +13 -13
- data/logging.gemspec +41 -0
- data/tasks/ann.rake +1 -1
- data/tasks/gem.rake +104 -28
- data/tasks/post_load.rake +4 -8
- data/tasks/rubyforge.rake +2 -4
- data/tasks/setup.rb +36 -7
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +3 -0
- data/test/appenders/test_buffered_io.rb +183 -0
- data/test/appenders/test_email.rb +27 -22
- data/test/appenders/test_io.rb +2 -0
- data/test/layouts/test_pattern.rb +5 -0
- data/test/test_layout.rb +9 -9
- metadata +19 -5
- data/Manifest.txt +0 -62
- data/tasks/manifest.rake +0 -48
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
|
-
|
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$
|
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.
|
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
|
data/lib/logging/appender.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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 <<
|
59
|
+
rfc822msg << buffer.join
|
114
60
|
|
115
61
|
### send email
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
data/lib/logging/appenders/io.rb
CHANGED
@@ -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? :
|
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?(
|
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
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|