filterfish-logging 0.9.8

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 (55) hide show
  1. data/History.txt +176 -0
  2. data/Manifest.txt +54 -0
  3. data/README.txt +93 -0
  4. data/Rakefile +28 -0
  5. data/data/logging.yaml +63 -0
  6. data/lib/logging.rb +288 -0
  7. data/lib/logging/appender.rb +257 -0
  8. data/lib/logging/appenders/console.rb +43 -0
  9. data/lib/logging/appenders/email.rb +131 -0
  10. data/lib/logging/appenders/file.rb +55 -0
  11. data/lib/logging/appenders/growl.rb +182 -0
  12. data/lib/logging/appenders/io.rb +81 -0
  13. data/lib/logging/appenders/rolling_file.rb +293 -0
  14. data/lib/logging/appenders/syslog.rb +202 -0
  15. data/lib/logging/config/yaml_configurator.rb +197 -0
  16. data/lib/logging/layout.rb +103 -0
  17. data/lib/logging/layouts/basic.rb +35 -0
  18. data/lib/logging/layouts/pattern.rb +292 -0
  19. data/lib/logging/log_event.rb +50 -0
  20. data/lib/logging/logger.rb +388 -0
  21. data/lib/logging/repository.rb +151 -0
  22. data/lib/logging/root_logger.rb +60 -0
  23. data/lib/logging/utils.rb +44 -0
  24. data/tasks/ann.rake +78 -0
  25. data/tasks/bones.rake +21 -0
  26. data/tasks/gem.rake +106 -0
  27. data/tasks/manifest.rake +49 -0
  28. data/tasks/notes.rake +22 -0
  29. data/tasks/post_load.rake +37 -0
  30. data/tasks/rdoc.rake +49 -0
  31. data/tasks/rubyforge.rake +57 -0
  32. data/tasks/setup.rb +253 -0
  33. data/tasks/svn.rake +45 -0
  34. data/tasks/test.rake +38 -0
  35. data/test/appenders/test_console.rb +40 -0
  36. data/test/appenders/test_email.rb +167 -0
  37. data/test/appenders/test_file.rb +94 -0
  38. data/test/appenders/test_growl.rb +115 -0
  39. data/test/appenders/test_io.rb +113 -0
  40. data/test/appenders/test_rolling_file.rb +187 -0
  41. data/test/appenders/test_syslog.rb +192 -0
  42. data/test/benchmark.rb +88 -0
  43. data/test/config/test_yaml_configurator.rb +41 -0
  44. data/test/layouts/test_basic.rb +44 -0
  45. data/test/layouts/test_pattern.rb +173 -0
  46. data/test/setup.rb +66 -0
  47. data/test/test_appender.rb +162 -0
  48. data/test/test_layout.rb +85 -0
  49. data/test/test_log_event.rb +81 -0
  50. data/test/test_logger.rb +589 -0
  51. data/test/test_logging.rb +250 -0
  52. data/test/test_repository.rb +123 -0
  53. data/test/test_root_logger.rb +82 -0
  54. data/test/test_utils.rb +48 -0
  55. metadata +126 -0
@@ -0,0 +1,202 @@
1
+ # $Id$
2
+
3
+ begin
4
+ require 'syslog'
5
+ HAVE_SYSLOG = true
6
+ rescue LoadError
7
+ HAVE_SYSLOG = false
8
+ end
9
+
10
+ # only load this class if we have the syslog library
11
+ # Windows does not have syslog
12
+ #
13
+ if HAVE_SYSLOG
14
+
15
+ module Logging::Appenders
16
+
17
+ # This class provides an Appender that can write to the UNIX syslog
18
+ # daemon.
19
+ #
20
+ class Syslog < ::Logging::Appender
21
+ include ::Syslog::Constants
22
+
23
+ # call-seq:
24
+ # Syslog.new( name, opts = {} )
25
+ #
26
+ # Create an appender that will log messages to the system message
27
+ # logger. The message is then written to the system console, log files,
28
+ # logged-in users, or forwarded to other machines as appropriate. The
29
+ # options that can be used to configure the appender are as follows:
30
+ #
31
+ # :ident => identifier string (name is used by default)
32
+ # :logopt => options used when opening the connection
33
+ # :facility => the syslog facility to use
34
+ #
35
+ # The parameter :ident is a string that will be prepended to every
36
+ # message. The :logopt argument is a bit field specifying logging
37
+ # options, which is formed by OR'ing one or more of the following
38
+ # values:
39
+ #
40
+ # LOG_CONS If syslog() cannot pass the message to syslogd(8) it
41
+ # wil attempt to write the message to the console
42
+ # ('/dev/console').
43
+ #
44
+ # LOG_NDELAY Open the connection to syslogd(8) immediately. Normally
45
+ # the open is delayed until the first message is logged.
46
+ # Useful for programs that need to manage the order in
47
+ # which file descriptors are allocated.
48
+ #
49
+ # LOG_PERROR Write the message to standard error output as well to
50
+ # the system log.
51
+ #
52
+ # LOG_PID Log the process id with each message: useful for
53
+ # identifying instantiations of daemons.
54
+ #
55
+ # The :facility parameter encodes a default facility to be assigned to
56
+ # all messages that do not have an explicit facility encoded:
57
+ #
58
+ # LOG_AUTH The authorization system: login(1), su(1), getty(8),
59
+ # etc.
60
+ #
61
+ # LOG_AUTHPRIV The same as LOG_AUTH, but logged to a file readable
62
+ # only by selected individuals.
63
+ #
64
+ # LOG_CONSOLE Messages written to /dev/console by the kernel console
65
+ # output driver.
66
+ #
67
+ # LOG_CRON The cron daemon: cron(8).
68
+ #
69
+ # LOG_DAEMON System daemons, such as routed(8), that are not
70
+ # provided for explicitly by other facilities.
71
+ #
72
+ # LOG_FTP The file transfer protocol daemons: ftpd(8), tftpd(8).
73
+ #
74
+ # LOG_KERN Messages generated by the kernel. These cannot be
75
+ # generated by any user processes.
76
+ #
77
+ # LOG_LPR The line printer spooling system: lpr(1), lpc(8),
78
+ # lpd(8), etc.
79
+ #
80
+ # LOG_MAIL The mail system.
81
+ #
82
+ # LOG_NEWS The network news system.
83
+ #
84
+ # LOG_SECURITY Security subsystems, such as ipfw(4).
85
+ #
86
+ # LOG_SYSLOG Messages generated internally by syslogd(8).
87
+ #
88
+ # LOG_USER Messages generated by random user processes. This is
89
+ # the default facility identifier if none is specified.
90
+ #
91
+ # LOG_UUCP The uucp system.
92
+ #
93
+ # LOG_LOCAL0 Reserved for local use. Similarly for LOG_LOCAL1
94
+ # through LOG_LOCAL7.
95
+ #
96
+ def initialize( name, opts = {} )
97
+ ident = opts.getopt(:ident, name)
98
+ logopt = opts.getopt(:logopt, (LOG_PID | LOG_CONS), :as => Integer)
99
+ facility = opts.getopt(:facility, LOG_USER, :as => Integer)
100
+ @syslog = ::Syslog.open(ident, logopt, facility)
101
+
102
+ # provides a mapping from the default Logging levels
103
+ # to the syslog levels
104
+ @map = [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT]
105
+
106
+ map = opts.getopt(:map)
107
+ self.map = map unless map.nil?
108
+
109
+ super
110
+ end
111
+
112
+ # call-seq:
113
+ # map = { logging_levels => syslog_levels }
114
+ #
115
+ # Configure the mapping from the Logging levels to the syslog levels.
116
+ # This is needed in order to log events at the proper syslog level.
117
+ #
118
+ # Without any configuration, the following maping will be used:
119
+ #
120
+ # :debug => LOG_DEBUG
121
+ # :info => LOG_INFO
122
+ # :warn => LOG_WARNING
123
+ # :error => LOG_ERR
124
+ # :fatal => LOG_CRIT
125
+ #
126
+ def map=( levels )
127
+ map = []
128
+ levels.keys.each do |lvl|
129
+ num = ::Logging.level_num(lvl)
130
+ map[num] = syslog_level_num(levels[lvl])
131
+ end
132
+ @map = map
133
+ end
134
+
135
+ # call-seq:
136
+ # close
137
+ #
138
+ # Closes the connetion to the syslog facility.
139
+ #
140
+ def close( footer = true )
141
+ super
142
+ @syslog.close
143
+ self
144
+ end
145
+
146
+ # call-seq:
147
+ # closed? => true or false
148
+ #
149
+ # Queries the connection to the syslog facility and returns +true+ if
150
+ # the connection is closed.
151
+ #
152
+ def closed?
153
+ !@syslog.opened?
154
+ end
155
+
156
+
157
+ private
158
+
159
+ # call-seq:
160
+ # write( event )
161
+ #
162
+ # Write the given _event_ to the syslog facility. The log event will be
163
+ # processed through the Layout assciated with this appender. The message
164
+ # will be logged at the level specified by the event.
165
+ #
166
+ def write( event )
167
+ pri = LOG_DEBUG
168
+ message = if event.instance_of?(::Logging::LogEvent)
169
+ pri = @map[event.level]
170
+ @layout.format(event)
171
+ else
172
+ event.to_s
173
+ end
174
+ return if message.empty?
175
+
176
+ @syslog.log(pri, '%s', message)
177
+ self
178
+ end
179
+
180
+ # call-seq:
181
+ # syslog_level_num( level ) => integer
182
+ #
183
+ # Takes the given _level_ as a string, symbol, or integer and returns
184
+ # the corresponding syslog level number.
185
+ #
186
+ def syslog_level_num( level )
187
+ case level
188
+ when Integer; level
189
+ when String, Symbol
190
+ level = level.to_s.upcase
191
+ self.class.const_get level
192
+ else
193
+ raise ArgumentError, "unkonwn level '#{level}'"
194
+ end
195
+ end
196
+
197
+ end # class Syslog
198
+
199
+ end # module Logging::Appenders
200
+ end # HAVE_SYSLOG
201
+
202
+ # EOF
@@ -0,0 +1,197 @@
1
+ # $Id$
2
+
3
+ require 'yaml'
4
+
5
+ module Logging
6
+ module Config
7
+
8
+ # The YamlConfigurator class is used to configure the Logging framework
9
+ # using information found in a YAML file.
10
+ #
11
+ class YamlConfigurator
12
+
13
+ class Error < StandardError; end # :nodoc:
14
+
15
+ class << self
16
+
17
+ # call-seq:
18
+ # YamlConfigurator.load( file, key = 'logging_config' )
19
+ #
20
+ # Load the given YAML _file_ and use it to configure the Logging
21
+ # framework. The file can be either a filename, and open File, or an
22
+ # IO object. If it is the latter two, the File / IO object will not be
23
+ # closed by this method.
24
+ #
25
+ # The configuration will be loaded from the given _key_ in the YAML
26
+ # stream.
27
+ #
28
+ def load( file, key = 'logging_config' )
29
+ io, close = nil, false
30
+ case file
31
+ when String
32
+ ext = File.extname(file)
33
+ if ext == 'yml' || ext == 'yaml'
34
+ io = File.open(file, 'r')
35
+ close = true
36
+ else
37
+ io = StringIO.new(file)
38
+ close = true
39
+ end
40
+ when IO
41
+ io = file
42
+ else
43
+ raise Error, 'expecting a filename or a File'
44
+ end
45
+
46
+ begin
47
+ new(io, key).load
48
+ ensure
49
+ io.close if close
50
+ end
51
+ nil
52
+ end
53
+ end # class << self
54
+
55
+ # call-seq:
56
+ # YamlConfigurator.new( io, key )
57
+ #
58
+ # Creates a new YAML configurator that will load the Logging
59
+ # configuration from the given _io_ stream. The configuration will be
60
+ # loaded from the given _key_ in the YAML stream.
61
+ #
62
+ def initialize( io, key )
63
+ YAML.load_documents(io) do |doc|
64
+ @config = doc[key]
65
+ break if @config.instance_of?(Hash)
66
+ end
67
+
68
+ unless @config.instance_of?(Hash)
69
+ raise Error, "Key '#{key}' not defined in YAML configuration"
70
+ end
71
+ end
72
+
73
+ # call-seq:
74
+ # load
75
+ #
76
+ # Loads the Logging configuration from the data loaded from the YAML
77
+ # file.
78
+ #
79
+ def load
80
+ pre_config @config['pre_config']
81
+ appenders @config['appenders']
82
+ loggers @config['loggers']
83
+ end
84
+
85
+ # call-seq:
86
+ # pre_config( config )
87
+ #
88
+ # Configures the logging levels, object format style, and root logging
89
+ # level.
90
+ #
91
+ def pre_config( config )
92
+ # if no pre_config section was given, just create an empty hash
93
+ # we do this to ensure that some logging levels are always defined
94
+ config ||= Hash.new
95
+
96
+ # define levels
97
+ levels = config['define_levels']
98
+ ::Logging.init(levels) unless levels.nil?
99
+
100
+ # format as
101
+ format = config['format_as']
102
+ ::Logging.format_as(format) unless format.nil?
103
+
104
+ # grab the root logger and set the logging level
105
+ root = ::Logging::Logger.root
106
+ if config.has_key?('root')
107
+ root.level = config['root']['level']
108
+ end
109
+ end
110
+
111
+ # call-seq:
112
+ # appenders( ary )
113
+ #
114
+ # Given an array of Appender configurations, this method will iterate
115
+ # over each and create the Appender(s).
116
+ #
117
+ def appenders( ary )
118
+ return if ary.nil?
119
+
120
+ ary.each {|h| appender(h)}
121
+ end
122
+
123
+ # call-seq:
124
+ # loggers( ary )
125
+ #
126
+ # Given an array of Logger configurations, this method will iterate over
127
+ # each and create the Logger(s).
128
+ #
129
+ def loggers( ary )
130
+ return if ary.nil?
131
+
132
+ ary.each do |config|
133
+ name = config['name']
134
+ raise Error, 'Logger name not given' if name.nil?
135
+
136
+ l = Logging::Logger.new name
137
+ l.level = config['level'] if config.has_key?('level')
138
+ l.additive = config['additive'] if l.respond_to? :additive=
139
+ l.trace = config['trace'] if l.respond_to? :trace=
140
+
141
+ if config.has_key?('appenders')
142
+ l.appenders = config['appenders'].map {|n| ::Logging::Appender[n]}
143
+ end
144
+ end
145
+ end
146
+
147
+ # call-seq:
148
+ # appender( config )
149
+ #
150
+ # Creates a new Appender based on the given _config_ options (a hash).
151
+ # The type of Appender created is determined by the 'type' option in the
152
+ # config. The remaining config options are passed to the Appender
153
+ # initializer.
154
+ #
155
+ # The config options can also contain a 'layout' option. This should be
156
+ # another set of options used to create a Layout for this Appender.
157
+ #
158
+ def appender( config )
159
+ return if config.nil?
160
+ config = config.dup
161
+
162
+ type = config.delete('type')
163
+ raise Error, 'Appender type not given' if type.nil?
164
+
165
+ name = config.delete('name')
166
+ raise Error, 'Appender name not given' if type.nil?
167
+
168
+ config['layout'] = layout(config.delete('layout'))
169
+
170
+ clazz = ::Logging::Appenders.const_get type
171
+ clazz.new(name, config)
172
+ end
173
+
174
+ # call-seq:
175
+ # layout( config )
176
+ #
177
+ # Creates a new Layout based on the given _config_ options (a hash).
178
+ # The type of Layout created is determined by the 'type' option in the
179
+ # config. The remaining config options are passed to the Layout
180
+ # initializer.
181
+ #
182
+ def layout( config )
183
+ return if config.nil?
184
+ config = config.dup
185
+
186
+ type = config.delete('type')
187
+ raise Error, 'Layout type not given' if type.nil?
188
+
189
+ clazz = ::Logging::Layouts.const_get type
190
+ clazz.new config
191
+ end
192
+
193
+ end # class YamlConfigurator
194
+ end # module Config
195
+ end # module Logging
196
+
197
+ # EOF
@@ -0,0 +1,103 @@
1
+ # $Id$
2
+
3
+ require 'yaml'
4
+
5
+ module Logging
6
+
7
+ # The +Layout+ class provides methods for formatting log events into a
8
+ # string representation. Layouts are used by Appenders to format log
9
+ # events before writing them to the logging destination.
10
+ #
11
+ # All other Layouts inherit from this class which provides stub methods.
12
+ # Each subclass should provide a +format+ method. A layout can be used by
13
+ # more than one +Appender+ so all the methods need to be thread safe.
14
+ #
15
+ class Layout
16
+
17
+ # call-seq:
18
+ # Layout.new( :format_as => :string )
19
+ #
20
+ # Creates a new layout that will format objecs as strings using the
21
+ # given <tt>:format_as</tt> style. This can be one of <tt>:string</tt>,
22
+ # <tt>:inspect</tt>, or <tt>:yaml</tt>. These formatting commands map to
23
+ # the following object methods:
24
+ #
25
+ # * :string => to_s
26
+ # * :inspect => inspect
27
+ # * :yaml => to_yaml
28
+ #
29
+ # If the format is not specified then the global object format is used
30
+ # (see Logging#format_as). If the global object format is not specified
31
+ # then <tt>:string</tt> is used.
32
+ #
33
+ def initialize( opts = {} )
34
+ default = ::Logging.const_defined?('OBJ_FORMAT') ?
35
+ ::Logging::OBJ_FORMAT : nil
36
+
37
+ f = opts.getopt(:format_as, default)
38
+ f = f.intern if f.instance_of? String
39
+
40
+ @obj_format = case f
41
+ when :inspect, :yaml; f
42
+ else :string end
43
+ end
44
+
45
+ # call-seq:
46
+ # format( event )
47
+ #
48
+ # Returns a string representation of the given loggging _event_. It is
49
+ # up to subclasses to implement this method.
50
+ #
51
+ def format( event ) nil end
52
+
53
+ # call-seq:
54
+ # header
55
+ #
56
+ # Returns a header string to be used at the beginning of a logging
57
+ # appender.
58
+ #
59
+ def header( ) '' end
60
+
61
+ # call-seq:
62
+ # footer
63
+ #
64
+ # Returns a footer string to be used at the end of a logging appender.
65
+ #
66
+ def footer( ) '' end
67
+
68
+
69
+ protected
70
+
71
+ # call-seq:
72
+ # format_obj( obj )
73
+ #
74
+ # Return a string representation of the given object. Depending upon
75
+ # the configuration of the logger system the format will be an +inspect+
76
+ # based represenation or a +yaml+ based representation.
77
+ #
78
+ def format_obj( obj )
79
+ case obj
80
+ when String; obj
81
+ when Exception
82
+ str = "<#{obj.class.name}> #{obj.message}"
83
+ unless obj.backtrace.nil?
84
+ str << "\n\t" << obj.backtrace.join("\n\t")
85
+ end
86
+ str
87
+ when nil; "<#{obj.class.name}> nil"
88
+ else
89
+ str = "<#{obj.class.name}> "
90
+ str << case @obj_format
91
+ when :inspect; obj.inspect
92
+ when :yaml; "\n#{obj.to_yaml}"
93
+ else obj.to_s end
94
+ str
95
+ end
96
+ end
97
+
98
+ end # class Layout
99
+ end # module Logging
100
+
101
+ Logging.require_all_libs_relative_to(__FILE__, 'layouts')
102
+
103
+ # EOF