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,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