sgeorgi-logging 1.4.2

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 (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,260 @@
1
+
2
+ module Logging
3
+
4
+ # The +Appender+ class is provides methods for appending log events to a
5
+ # logging destination. The log events are formatted into strings using a
6
+ # Layout.
7
+ #
8
+ # All other Appenders inherit from this class which provides stub methods.
9
+ # Each subclass should provide a +write+ method that will write log
10
+ # messages to the logging destination.
11
+ #
12
+ # A private +sync+ method is provided for use by subclasses. It is used to
13
+ # synchronize writes to the logging destination, and can be used by
14
+ # subclasses to synchronize the closing or flushing of the logging
15
+ # destination.
16
+ #
17
+ class Appender
18
+
19
+ attr_reader :name, :layout, :level
20
+
21
+ # call-seq:
22
+ # Appender.new( name )
23
+ # Appender.new( name, :layout => layout )
24
+ #
25
+ # Creates a new appender using the given name. If no Layout is specified,
26
+ # then a Basic layout will be used. Any logging header supplied by the
27
+ # layout will be written to the logging destination when the Appender is
28
+ # created.
29
+ #
30
+ # Options:
31
+ #
32
+ # :layout => the layout to use when formatting log events
33
+ # :level => the level at which to log
34
+ #
35
+ def initialize( name, opts = {} )
36
+ ::Logging.init unless ::Logging.const_defined? :MAX_LEVEL_LENGTH
37
+
38
+ @name = name.to_s
39
+ @closed = false
40
+
41
+ self.layout = opts.getopt(:layout, ::Logging::Layouts::Basic.new)
42
+ self.level = opts.getopt(:level)
43
+
44
+ @mutex = ReentrantMutex.new
45
+ header = @layout.header
46
+
47
+ unless header.nil? || header.empty?
48
+ begin
49
+ sync {write(header)}
50
+ rescue StandardError => err
51
+ ::Logging.log_internal(-2) {err}
52
+ end
53
+ end
54
+
55
+ ::Logging::Appenders[@name] = self
56
+ end
57
+
58
+ # call-seq:
59
+ # append( event )
60
+ #
61
+ # Write the given _event_ to the logging destination. The log event will
62
+ # be processed through the Layout associated with the Appender.
63
+ #
64
+ def append( event )
65
+ if @closed
66
+ raise RuntimeError,
67
+ "appender '<#{self.class.name}: #{@name}>' is closed"
68
+ end
69
+
70
+ # only append if the event level is less than or equal to the configured
71
+ # appender level
72
+ unless @level > event.level
73
+ begin
74
+ sync {write(event)}
75
+ rescue StandardError => err
76
+ ::Logging.log_internal(-2) {err}
77
+ end
78
+ end
79
+
80
+ self
81
+ end
82
+
83
+ # call-seq:
84
+ # appender << string
85
+ #
86
+ # Write the given _string_ to the logging destination "as is" -- no
87
+ # layout formatting will be performed.
88
+ #
89
+ def <<( str )
90
+ if @closed
91
+ raise RuntimeError,
92
+ "appender '<#{self.class.name}: #{@name}>' is closed"
93
+ end
94
+
95
+ unless @level >= ::Logging::LEVELS.length
96
+ begin
97
+ sync {write(str)}
98
+ rescue StandardError => err
99
+ ::Logging.log_internal(-2) {err}
100
+ end
101
+ end
102
+ self
103
+ end
104
+
105
+ # call-seq:
106
+ # level = :all
107
+ #
108
+ # Set the level for this appender; log events below this level will be
109
+ # ignored by this appender. The level can be either a +String+, a
110
+ # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
111
+ # the case.
112
+ #
113
+ # There are two special levels -- "all" and "off". The former will
114
+ # enable recording of all log events. The latter will disable the
115
+ # recording of all events.
116
+ #
117
+ # Example:
118
+ #
119
+ # appender.level = :debug
120
+ # appender.level = "INFO"
121
+ # appender.level = 4
122
+ # appender.level = 'off'
123
+ # appender.level = :all
124
+ #
125
+ # These prodcue an +ArgumentError+
126
+ #
127
+ # appender.level = Object
128
+ # appender.level = -1
129
+ # appender.level = 1_000_000_000_000
130
+ #
131
+ def level=( level )
132
+ lvl = case level
133
+ when String, Symbol; ::Logging::level_num(level)
134
+ when Fixnum; level
135
+ when nil; 0
136
+ else
137
+ raise ArgumentError,
138
+ "level must be a String, Symbol, or Integer"
139
+ end
140
+ if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
141
+ raise ArgumentError, "unknown level was given '#{level}'"
142
+ end
143
+
144
+ @level = lvl
145
+ end
146
+
147
+ # call-seq
148
+ # appender.layout = Logging::Layouts::Basic.new
149
+ #
150
+ # Sets the layout to be used by this appender.
151
+ #
152
+ def layout=( layout )
153
+ unless layout.kind_of? ::Logging::Layout
154
+ raise TypeError,
155
+ "#{layout.inspect} is not a kind of 'Logging::Layout'"
156
+ end
157
+ @layout = layout
158
+ end
159
+
160
+ # call-seq:
161
+ # close( footer = true )
162
+ #
163
+ # Close the appender and writes the layout footer to the logging
164
+ # destination if the _footer_ flag is set to +true+. Log events will
165
+ # no longer be written to the logging destination after the appender
166
+ # is closed.
167
+ #
168
+ def close( footer = true )
169
+ return self if @closed
170
+ ::Logging::Appenders.remove(@name)
171
+ @closed = true
172
+
173
+ sync {flush}
174
+
175
+ if footer
176
+ footer = @layout.footer
177
+ unless footer.nil? || footer.empty?
178
+ begin
179
+ sync {write(footer)}
180
+ rescue StandardError => err
181
+ ::Logging.log_internal(-2) {err}
182
+ end
183
+ end
184
+ end
185
+ self
186
+ end
187
+
188
+ # call-seq:
189
+ # closed?
190
+ #
191
+ # Returns +true+ if the appender has been closed; returns +false+
192
+ # otherwise. When an appender is closed, no more log events can be
193
+ # written to the logging destination.
194
+ #
195
+ def closed?
196
+ @closed
197
+ end
198
+
199
+ # Reopen the connection to the underlying logging destination. If the
200
+ # connection is currently closed then it will be opened. If the connection
201
+ # is currently open then it will be closed and immediately opened.
202
+ #
203
+ def reopen
204
+ @closed = false
205
+ self
206
+ end
207
+
208
+ # call-seq:
209
+ # flush
210
+ #
211
+ # Call +flush+ to force an appender to write out any buffered log events.
212
+ # Similar to IO#flush, so use in a similar fashion.
213
+ #
214
+ def flush
215
+ self
216
+ end
217
+
218
+ # call-seq:
219
+ # inspect => string
220
+ #
221
+ # Returns a string representation of the appender.
222
+ #
223
+ def inspect
224
+ "<%s:0x%x name=\"%s\">" % [
225
+ self.class.name.sub(%r/^Logging::/, ''),
226
+ self.object_id,
227
+ self.name
228
+ ]
229
+ end
230
+
231
+
232
+ private
233
+
234
+ # call-seq:
235
+ # write( event )
236
+ #
237
+ # Writes the given _event_ to the logging destination. Subclasses should
238
+ # provide an implementation of this method. The _event_ can be either a
239
+ # LogEvent or a String. If a LogEvent, then it will be formatted using
240
+ # the layout given to the appender when it was created.
241
+ #
242
+ def write( event )
243
+ nil
244
+ end
245
+
246
+ # call-seq:
247
+ # sync { block }
248
+ #
249
+ # Obtains an exclusive lock, runs the block, and releases the lock when
250
+ # the block completes. This method is re-entrant so that a single thread
251
+ # can call +sync+ multiple times without hanging the thread.
252
+ #
253
+ def sync( &block )
254
+ @mutex.synchronize(&block)
255
+ end
256
+
257
+ end # class Appender
258
+ end # module Logging
259
+
260
+ # EOF
@@ -0,0 +1,137 @@
1
+
2
+ module Logging
3
+ module Appenders
4
+
5
+ # Accessor / Factory for the Email appender.
6
+ #
7
+ def email( *args )
8
+ return ::Logging::Appenders::Email if args.empty?
9
+ ::Logging::Appenders::Email.new(*args)
10
+ end
11
+
12
+ # Accessor / Factory for the File appender.
13
+ #
14
+ def file( *args )
15
+ return ::Logging::Appenders::File if args.empty?
16
+ ::Logging::Appenders::File.new(*args)
17
+ end
18
+
19
+ # Accessor / Factory for the Growl appender.
20
+ #
21
+ def growl( *args )
22
+ return ::Logging::Appenders::Growl if args.empty?
23
+ ::Logging::Appenders::Growl.new(*args)
24
+ end
25
+
26
+ # Accessor / Factory for the IO appender.
27
+ #
28
+ def io( *args )
29
+ return ::Logging::Appenders::IO if args.empty?
30
+ ::Logging::Appenders::IO.new(*args)
31
+ end
32
+
33
+ # Accessor / Factory for the RollingFile appender.
34
+ #
35
+ def rolling_file( *args )
36
+ return ::Logging::Appenders::RollingFile if args.empty?
37
+ ::Logging::Appenders::RollingFile.new(*args)
38
+ end
39
+
40
+ # Accessor / Factory for the Stderr appender.
41
+ #
42
+ def stderr( *args )
43
+ if args.empty?
44
+ return self['stderr'] || ::Logging::Appenders::Stderr.new
45
+ end
46
+ ::Logging::Appenders::Stderr.new(*args)
47
+ end
48
+
49
+ # Accessor / Factory for the Stdout appender.
50
+ #
51
+ def stdout( *args )
52
+ if args.empty?
53
+ return self['stdout'] || ::Logging::Appenders::Stdout.new
54
+ end
55
+ ::Logging::Appenders::Stdout.new(*args)
56
+ end
57
+
58
+ # Accessor / Factory for the StringIo appender.
59
+ #
60
+ def string_io( *args )
61
+ return ::Logging::Appenders::StringIo if args.empty?
62
+ ::Logging::Appenders::StringIo.new(*args)
63
+ end
64
+
65
+ if HAVE_SYSLOG
66
+ # Accessor / Factory for the Syslog appender.
67
+ #
68
+ def syslog( *args )
69
+ return ::Logging::Appenders::Syslog if args.empty?
70
+ ::Logging::Appenders::Syslog.new(*args)
71
+ end
72
+ end
73
+
74
+ # call-seq:
75
+ # Appenders[name]
76
+ #
77
+ # Returns the appender instance stroed in the appender hash under the
78
+ # key _name_, or +nil+ if no appender has been created using that name.
79
+ #
80
+ def []( name ) @appenders[name] end
81
+
82
+ # call-seq:
83
+ # Appenders[name] = appender
84
+ #
85
+ # Stores the given _appender_ instance in the appender hash under the
86
+ # key _name_.
87
+ #
88
+ def []=( name, value ) @appenders[name] = value end
89
+
90
+ # call-seq:
91
+ # Appenders.remove( name )
92
+ #
93
+ # Removes the appender instance stored in the appender hash under the
94
+ # key _name_.
95
+ #
96
+ def remove( name ) @appenders.delete(name) end
97
+
98
+ # call-seq:
99
+ # each {|appender| block}
100
+ #
101
+ # Yield each appender to the _block_.
102
+ #
103
+ def each( &block )
104
+ @appenders.values.each(&block)
105
+ return nil
106
+ end
107
+
108
+ # :stopdoc:
109
+ def reset
110
+ @appenders.values.each {|appender|
111
+ next if appender.nil?
112
+ appender.close
113
+ }
114
+ @appenders.clear
115
+ return nil
116
+ end
117
+ # :startdoc:
118
+
119
+ extend self
120
+ @appenders = Hash.new
121
+
122
+ end # module Appenders
123
+ end # module Logging
124
+
125
+ Logging.libpath {
126
+ require 'logging/appenders/buffering'
127
+ require 'logging/appenders/io'
128
+ require 'logging/appenders/console'
129
+ require 'logging/appenders/email'
130
+ require 'logging/appenders/file'
131
+ require 'logging/appenders/growl'
132
+ require 'logging/appenders/rolling_file'
133
+ require 'logging/appenders/string_io'
134
+ require 'logging/appenders/syslog'
135
+ }
136
+
137
+ # EOF
@@ -0,0 +1,178 @@
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
+ ::Logging.init unless ::Logging.const_defined? :MAX_LEVEL_LENGTH
121
+
122
+ @buffer = []
123
+ self.immediate_at = opts.getopt(:immediate_at, '')
124
+ self.auto_flushing = opts.getopt(:auto_flushing, true)
125
+ end
126
+
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
+ # Returns true if the _event_ level matches one of the configured
142
+ # immediate logging levels. Otherwise returns false.
143
+ #
144
+ def immediate?( event )
145
+ return false unless event.respond_to? :level
146
+ @immediate[event.level]
147
+ end
148
+
149
+
150
+ private
151
+
152
+ # call-seq:
153
+ # write( event )
154
+ #
155
+ # Writes the given _event_ to the logging destination. The _event_ can
156
+ # be either a LogEvent or a String. If a LogEvent, then it will be
157
+ # formatted using the layout given to the appender when it was created.
158
+ #
159
+ def write( event )
160
+ add_to_buffer event
161
+ self
162
+ end
163
+
164
+ #
165
+ # Explicit method name to call the write method from RollingFileAppender's write method
166
+ # to overcome "The newer patches for 1.8.7 introduced a regression that breaks
167
+ # calls to "super" within a block." (TwP on March 23rd, 2010)
168
+ #
169
+ def write_explicit( event )
170
+ add_to_buffer event
171
+ self
172
+ end
173
+
174
+
175
+ end # module Buffering
176
+ end # module Logging::Appenders
177
+
178
+ # EOF