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,327 @@
1
+
2
+ module Logging::Appenders
3
+
4
+ # An appender that writes to a file and ensures that the file size or age
5
+ # never exceeds some user specified level.
6
+ #
7
+ # The goal of this class is to write log messages to a file. When the file
8
+ # age or size exceeds a given limit then the log file is copied and then
9
+ # truncated. The name of the copy indicates it is an older log file.
10
+ #
11
+ # The name of the log file is changed by inserting the age of the log file
12
+ # (as a single number) between the log file name and the extension. If the
13
+ # file has no extension then the number is appended to the filename. Here
14
+ # is a simple example:
15
+ #
16
+ # /var/log/ruby.log => /var/log/ruby.1.log
17
+ #
18
+ # New log messages will continue to be appended to the same log file
19
+ # (<tt>/var/log/ruby.log</tt> in our example above). The age number for all
20
+ # older log files is incremented when the log file is rolled. The number of
21
+ # older log files to keep can be given, otherwise all the log files are
22
+ # kept.
23
+ #
24
+ # The actual process of rolling all the log file names can be expensive if
25
+ # there are many, many older log files to process.
26
+ #
27
+ # If you do not wish to use numbered files when rolling, you can specify the
28
+ # :roll_by option as 'date'. This will use a date/time stamp to
29
+ # differentiate the older files from one another. If you configure your
30
+ # rolling file appender to roll daily and ignore the file size:
31
+ #
32
+ # /var/log/ruby.log => /var/log/ruby.20091225.log
33
+ #
34
+ # Where the date is expressed as <tt>%Y%m%d</tt> in the Time#strftime format.
35
+ #
36
+ # NOTE: this class is not safe to use when log messages are written to files
37
+ # on NFS mounts or other remote file system. It should only be used for log
38
+ # files on the local file system. The exception to this is when a single
39
+ # process is writing to the log file; remote file systems are safe to
40
+ # use in this case but still not recommended.
41
+ #
42
+ class RollingFile < ::Logging::Appenders::IO
43
+
44
+ # call-seq:
45
+ # RollingFile.new( name, opts )
46
+ #
47
+ # Creates a new Rolling File Appender. The _name_ is the unique Appender
48
+ # name used to retrieve this appender from the Appender hash. The only
49
+ # required option is the filename to use for creating log files.
50
+ #
51
+ # [:filename] The base filename to use when constructing new log
52
+ # filenames.
53
+ #
54
+ # The following options are optional:
55
+ #
56
+ # [:layout] The Layout that will be used by this appender. The Basic
57
+ # layout will be used if none is given.
58
+ # [:truncate] When set to true any existing log files will be rolled
59
+ # immediately and a new, empty log file will be created.
60
+ # [:size] The maximum allowed size (in bytes) of a log file before
61
+ # it is rolled.
62
+ # [:age] The maximum age (in seconds) of a log file before it is
63
+ # rolled. The age can also be given as 'daily', 'weekly',
64
+ # or 'monthly'.
65
+ # [:keep] The number of rolled log files to keep.
66
+ # [:roll_by] How to name the rolled log files. This can be 'number' or
67
+ # 'date'.
68
+ #
69
+ def initialize( name, opts = {} )
70
+ # raise an error if a filename was not given
71
+ @fn = opts.getopt(:filename, name)
72
+ @fn_copy = @fn + '._copy_'
73
+ raise ArgumentError, 'no filename was given' if @fn.nil?
74
+ ::Logging::Appenders::File.assert_valid_logfile(@fn)
75
+
76
+ # grab our options
77
+ @size = opts.getopt(:size, :as => Integer)
78
+
79
+ code = 'def sufficiently_aged?() false end'
80
+ @age_fn = @fn + '.age'
81
+ @age_fn_mtime = nil
82
+
83
+ case @age = opts.getopt(:age)
84
+ when 'daily'
85
+ code = <<-CODE
86
+ def sufficiently_aged?
87
+ @age_fn_mtime ||= ::File.mtime(@age_fn)
88
+ now = Time.now
89
+ if (now.day != @age_fn_mtime.day) or (now - @age_fn_mtime) > 86400
90
+ return true
91
+ end
92
+ false
93
+ end
94
+ CODE
95
+ when 'weekly'
96
+ code = <<-CODE
97
+ def sufficiently_aged?
98
+ @age_fn_mtime ||= ::File.mtime(@age_fn)
99
+ if (Time.now - @age_fn_mtime) > 604800
100
+ return true
101
+ end
102
+ false
103
+ end
104
+ CODE
105
+ when 'monthly'
106
+ code = <<-CODE
107
+ def sufficiently_aged?
108
+ @age_fn_mtime ||= ::File.mtime(@age_fn)
109
+ now = Time.now
110
+ if (now.month != @age_fn_mtime.month) or (now - @age_fn_mtime) > 2678400
111
+ return true
112
+ end
113
+ false
114
+ end
115
+ CODE
116
+ when Integer, String
117
+ @age = Integer(@age)
118
+ code = <<-CODE
119
+ def sufficiently_aged?
120
+ @age_fn_mtime ||= ::File.mtime(@age_fn)
121
+ if (Time.now - @age_fn_mtime) > @age
122
+ return true
123
+ end
124
+ false
125
+ end
126
+ CODE
127
+ end
128
+
129
+ FileUtils.touch(@age_fn) if @age and !test(?f, @age_fn)
130
+
131
+ meta = class << self; self end
132
+ meta.class_eval code, __FILE__, __LINE__
133
+
134
+ super(name, ::File.new(@fn, 'a'), opts)
135
+
136
+ # setup the file roller
137
+ @roller =
138
+ case opts.getopt(:roll_by)
139
+ when 'number'; NumberedRoller.new(@fn, opts)
140
+ when 'date'; DateRoller.new(@fn, opts)
141
+ else
142
+ (@age and !@size) ?
143
+ DateRoller.new(@fn, opts) :
144
+ NumberedRoller.new(@fn, opts)
145
+ end
146
+
147
+ # if the truncate flag was set to true, then roll
148
+ roll_now = opts.getopt(:truncate, false)
149
+ if roll_now
150
+ copy_truncate
151
+ @roller.roll_files
152
+ end
153
+ end
154
+
155
+ # Returns the path to the logfile.
156
+ #
157
+ def filename() @fn.dup end
158
+
159
+ # Reopen the connection to the underlying logging destination. If the
160
+ # connection is currently closed then it will be opened. If the connection
161
+ # is currently open then it will be closed and immediately opened.
162
+ #
163
+ def reopen
164
+ @mutex.synchronize {
165
+ if defined? @io and @io
166
+ flush
167
+ @io.close rescue nil
168
+ end
169
+ @closed = false
170
+ @io = ::File.new(@fn, 'a')
171
+ @io.sync = true
172
+ }
173
+ self
174
+ end
175
+
176
+
177
+ private
178
+
179
+ # call-seq:
180
+ # write( event )
181
+ #
182
+ # Write the given _event_ to the log file. The log file will be rolled
183
+ # if the maximum file size is exceeded or if the file is older than the
184
+ # maximum age.
185
+ #
186
+ def write( event )
187
+ str = event.instance_of?(::Logging::LogEvent) ?
188
+ @layout.format(event) : event.to_s
189
+ return if str.empty?
190
+
191
+ @io.flock_sh { write_explicit(str) }
192
+
193
+ if roll_required?
194
+ @io.flock? {
195
+ @age_fn_mtime = nil
196
+ copy_truncate if roll_required?
197
+ }
198
+ @roller.roll_files
199
+ end
200
+ end
201
+
202
+ # Returns +true+ if the log file needs to be rolled.
203
+ #
204
+ def roll_required?
205
+ return false if ::File.exist? @fn_copy
206
+
207
+ # check if max size has been exceeded
208
+ s = @size ? ::File.size(@fn) > @size : false
209
+
210
+ # check if max age has been exceeded
211
+ a = sufficiently_aged?
212
+
213
+ return (s || a)
214
+ end
215
+
216
+ # Copy the contents of the logfile to another file. Truncate the logfile
217
+ # to zero length. This method will set the roll flag so that all the
218
+ # current logfiles will be rolled along with the copied file.
219
+ #
220
+ def copy_truncate
221
+ return unless ::File.exist?(@fn)
222
+ FileUtils.copy @fn, @fn_copy
223
+ @io.truncate 0
224
+
225
+ # touch the age file if needed
226
+ if @age
227
+ FileUtils.touch @age_fn
228
+ @age_fn_mtime = nil
229
+ end
230
+
231
+ @roller.roll = true
232
+ end
233
+
234
+
235
+ # :stopdoc:
236
+ class NumberedRoller
237
+ attr_accessor :roll
238
+
239
+ def initialize( fn, opts )
240
+ # grab the information we need to properly roll files
241
+ ext = ::File.extname(fn)
242
+ bn = ::File.join(::File.dirname(fn), ::File.basename(fn, ext))
243
+ @rgxp = %r/\.(\d+)#{Regexp.escape(ext)}\z/
244
+ @glob = "#{bn}.*#{ext}"
245
+ @logname_fmt = "#{bn}.%d#{ext}"
246
+ @fn_copy = fn + '._copy_'
247
+ @keep = opts.getopt(:keep, :as => Integer)
248
+ @roll = false
249
+ end
250
+
251
+ def roll_files
252
+ return unless @roll and ::File.exist?(@fn_copy)
253
+
254
+ files = Dir.glob(@glob).find_all {|fn| @rgxp =~ fn}
255
+ unless files.empty?
256
+ # sort the files in revese order based on their count number
257
+ files = files.sort do |a,b|
258
+ a = Integer(@rgxp.match(a)[1])
259
+ b = Integer(@rgxp.match(b)[1])
260
+ b <=> a
261
+ end
262
+
263
+ # for each file, roll its count number one higher
264
+ files.each do |fn|
265
+ cnt = Integer(@rgxp.match(fn)[1])
266
+ if @keep and cnt >= @keep
267
+ ::File.delete fn
268
+ next
269
+ end
270
+ ::File.rename fn, sprintf(@logname_fmt, cnt+1)
271
+ end
272
+ end
273
+
274
+ # finally reanme the copied log file
275
+ ::File.rename(@fn_copy, sprintf(@logname_fmt, 1))
276
+ ensure
277
+ @roll = false
278
+ end
279
+ end
280
+
281
+ class DateRoller
282
+ attr_accessor :roll
283
+
284
+ def initialize( fn, opts )
285
+ @fn_copy = fn + '._copy_'
286
+ @roll = false
287
+ @keep = opts.getopt(:keep, :as => Integer)
288
+
289
+ ext = ::File.extname(fn)
290
+ bn = ::File.join(::File.dirname(fn), ::File.basename(fn, ext))
291
+
292
+ if @keep
293
+ @rgxp = %r/\.(\d+)(-\d+)?#{Regexp.escape(ext)}\z/
294
+ @glob = "#{bn}.*#{ext}"
295
+ end
296
+
297
+ if %w[daily weekly monthly].include?(opts.getopt(:age)) and !opts.getopt(:size)
298
+ @logname_fmt = "#{bn}.%Y%m%d#{ext}"
299
+ else
300
+ @logname_fmt = "#{bn}.%Y%m%d-%H%M%S#{ext}"
301
+ end
302
+ end
303
+
304
+ def roll_files
305
+ return unless @roll and ::File.exist?(@fn_copy)
306
+
307
+ # reanme the copied log file
308
+ ::File.rename(@fn_copy, Time.now.strftime(@logname_fmt))
309
+
310
+ # prune old log files
311
+ if @keep
312
+ files = Dir.glob(@glob).find_all {|fn| @rgxp =~ fn}
313
+ length = files.length
314
+ if length > @keep
315
+ files.sort {|a,b| b <=> a}.last(length-@keep).each {|fn| ::File.delete fn}
316
+ end
317
+ end
318
+ ensure
319
+ @roll = false
320
+ end
321
+ end
322
+ # :startdoc:
323
+
324
+ end # class RollingFile
325
+ end # module Logging::Appenders
326
+
327
+ # EOF
@@ -0,0 +1,68 @@
1
+
2
+ module Logging::Appenders
3
+
4
+ # This class provides an Appender that can write to a StringIO instance.
5
+ # This is very useful for testing log message output.
6
+ #
7
+ class StringIo < ::Logging::Appenders::IO
8
+
9
+ # The StringIO instance the appender is writing to.
10
+ attr_reader :sio
11
+
12
+ # call-seq:
13
+ # StringIo.new( name, opts = {} )
14
+ #
15
+ # Creates a new StrinIo appender that will append log messages to a
16
+ # StringIO instance.
17
+ #
18
+ def initialize( name, opts = {} )
19
+ @sio = StringIO.new
20
+ @sio.extend IoToS
21
+ @pos = 0
22
+ super(name, @sio, opts)
23
+ end
24
+
25
+ # Clears the internal StringIO instance. All log messages are removed
26
+ # from the buffer.
27
+ #
28
+ def clear
29
+ sync {
30
+ @pos = 0
31
+ @sio.seek 0
32
+ @sio.truncate 0
33
+ }
34
+ end
35
+ alias :reset :clear
36
+
37
+ %w[read readline readlines].each do|m|
38
+ class_eval <<-CODE
39
+ def #{m}( *args )
40
+ sync {
41
+ begin
42
+ @sio.seek @pos
43
+ rv = @sio.#{m}(*args)
44
+ @pos = @sio.tell
45
+ rv
46
+ rescue EOFError
47
+ nil
48
+ end
49
+ }
50
+ end
51
+ CODE
52
+ end
53
+
54
+ # :stopdoc:
55
+ module IoToS
56
+ def to_s
57
+ seek 0
58
+ str = read
59
+ seek 0
60
+ return str
61
+ end
62
+ end
63
+ # :startdoc:
64
+
65
+ end # class StringIo
66
+ end # module Logging::Appenders
67
+
68
+ # EOF
@@ -0,0 +1,210 @@
1
+
2
+ # only load this class if we have the syslog library
3
+ # Windows does not have syslog
4
+ #
5
+ if HAVE_SYSLOG
6
+
7
+ module Logging::Appenders
8
+
9
+ # This class provides an Appender that can write to the UNIX syslog
10
+ # daemon.
11
+ #
12
+ class Syslog < ::Logging::Appender
13
+ include ::Syslog::Constants
14
+
15
+ # call-seq:
16
+ # Syslog.new( name, opts = {} )
17
+ #
18
+ # Create an appender that will log messages to the system message
19
+ # logger. The message is then written to the system console, log files,
20
+ # logged-in users, or forwarded to other machines as appropriate. The
21
+ # options that can be used to configure the appender are as follows:
22
+ #
23
+ # :ident => identifier string (name is used by default)
24
+ # :logopt => options used when opening the connection
25
+ # :facility => the syslog facility to use
26
+ #
27
+ # The parameter :ident is a string that will be prepended to every
28
+ # message. The :logopt argument is a bit field specifying logging
29
+ # options, which is formed by OR'ing one or more of the following
30
+ # values:
31
+ #
32
+ # LOG_CONS If syslog() cannot pass the message to syslogd(8) it
33
+ # wil attempt to write the message to the console
34
+ # ('/dev/console').
35
+ #
36
+ # LOG_NDELAY Open the connection to syslogd(8) immediately. Normally
37
+ # the open is delayed until the first message is logged.
38
+ # Useful for programs that need to manage the order in
39
+ # which file descriptors are allocated.
40
+ #
41
+ # LOG_PERROR Write the message to standard error output as well to
42
+ # the system log.
43
+ #
44
+ # LOG_PID Log the process id with each message: useful for
45
+ # identifying instantiations of daemons.
46
+ #
47
+ # The :facility parameter encodes a default facility to be assigned to
48
+ # all messages that do not have an explicit facility encoded:
49
+ #
50
+ # LOG_AUTH The authorization system: login(1), su(1), getty(8),
51
+ # etc.
52
+ #
53
+ # LOG_AUTHPRIV The same as LOG_AUTH, but logged to a file readable
54
+ # only by selected individuals.
55
+ #
56
+ # LOG_CONSOLE Messages written to /dev/console by the kernel console
57
+ # output driver.
58
+ #
59
+ # LOG_CRON The cron daemon: cron(8).
60
+ #
61
+ # LOG_DAEMON System daemons, such as routed(8), that are not
62
+ # provided for explicitly by other facilities.
63
+ #
64
+ # LOG_FTP The file transfer protocol daemons: ftpd(8), tftpd(8).
65
+ #
66
+ # LOG_KERN Messages generated by the kernel. These cannot be
67
+ # generated by any user processes.
68
+ #
69
+ # LOG_LPR The line printer spooling system: lpr(1), lpc(8),
70
+ # lpd(8), etc.
71
+ #
72
+ # LOG_MAIL The mail system.
73
+ #
74
+ # LOG_NEWS The network news system.
75
+ #
76
+ # LOG_SECURITY Security subsystems, such as ipfw(4).
77
+ #
78
+ # LOG_SYSLOG Messages generated internally by syslogd(8).
79
+ #
80
+ # LOG_USER Messages generated by random user processes. This is
81
+ # the default facility identifier if none is specified.
82
+ #
83
+ # LOG_UUCP The uucp system.
84
+ #
85
+ # LOG_LOCAL0 Reserved for local use. Similarly for LOG_LOCAL1
86
+ # through LOG_LOCAL7.
87
+ #
88
+ def initialize( name, opts = {} )
89
+ @ident = opts.getopt(:ident, name)
90
+ @logopt = opts.getopt(:logopt, (LOG_PID | LOG_CONS), :as => Integer)
91
+ @facility = opts.getopt(:facility, LOG_USER, :as => Integer)
92
+ @syslog = ::Syslog.open(@ident, @logopt, @facility)
93
+
94
+ # provides a mapping from the default Logging levels
95
+ # to the syslog levels
96
+ @map = [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT]
97
+
98
+ map = opts.getopt(:map)
99
+ self.map = map unless map.nil?
100
+
101
+ super
102
+ end
103
+
104
+ # call-seq:
105
+ # map = { logging_levels => syslog_levels }
106
+ #
107
+ # Configure the mapping from the Logging levels to the syslog levels.
108
+ # This is needed in order to log events at the proper syslog level.
109
+ #
110
+ # Without any configuration, the following maping will be used:
111
+ #
112
+ # :debug => LOG_DEBUG
113
+ # :info => LOG_INFO
114
+ # :warn => LOG_WARNING
115
+ # :error => LOG_ERR
116
+ # :fatal => LOG_CRIT
117
+ #
118
+ def map=( levels )
119
+ map = []
120
+ levels.keys.each do |lvl|
121
+ num = ::Logging.level_num(lvl)
122
+ map[num] = syslog_level_num(levels[lvl])
123
+ end
124
+ @map = map
125
+ end
126
+
127
+ # call-seq:
128
+ # close
129
+ #
130
+ # Closes the connetion to the syslog facility.
131
+ #
132
+ def close( footer = true )
133
+ super
134
+ @syslog.close
135
+ self
136
+ end
137
+
138
+ # call-seq:
139
+ # closed? => true or false
140
+ #
141
+ # Queries the connection to the syslog facility and returns +true+ if
142
+ # the connection is closed.
143
+ #
144
+ def closed?
145
+ !@syslog.opened?
146
+ end
147
+
148
+ # Reopen the connection to the underlying logging destination. If the
149
+ # connection is currently closed then it will be opened. If the connection
150
+ # is currently open then it will be closed and immediately opened.
151
+ #
152
+ def reopen
153
+ @mutex.synchronize {
154
+ if @syslog.opened?
155
+ flush
156
+ @syslog.close
157
+ end
158
+ @syslog = ::Syslog.open(@ident, @logopt, @facility)
159
+ @closed = false
160
+ }
161
+ self
162
+ end
163
+
164
+
165
+ private
166
+
167
+ # call-seq:
168
+ # write( event )
169
+ #
170
+ # Write the given _event_ to the syslog facility. The log event will be
171
+ # processed through the Layout assciated with this appender. The message
172
+ # will be logged at the level specified by the event.
173
+ #
174
+ def write( event )
175
+ pri = LOG_DEBUG
176
+ message = if event.instance_of?(::Logging::LogEvent)
177
+ pri = @map[event.level]
178
+ @layout.format(event)
179
+ else
180
+ event.to_s
181
+ end
182
+ return if message.empty?
183
+
184
+ @syslog.log(pri, '%s', message)
185
+ self
186
+ end
187
+
188
+ # call-seq:
189
+ # syslog_level_num( level ) => integer
190
+ #
191
+ # Takes the given _level_ as a string, symbol, or integer and returns
192
+ # the corresponding syslog level number.
193
+ #
194
+ def syslog_level_num( level )
195
+ case level
196
+ when Integer; level
197
+ when String, Symbol
198
+ level = level.to_s.upcase
199
+ self.class.const_get level
200
+ else
201
+ raise ArgumentError, "unkonwn level '#{level}'"
202
+ end
203
+ end
204
+
205
+ end # class Syslog
206
+
207
+ end # module Logging::Appenders
208
+ end # HAVE_SYSLOG
209
+
210
+ # EOF