sgeorgi-logging 1.4.2

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