logging 0.4.0 → 0.5.0

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.
@@ -0,0 +1,206 @@
1
+ # $Id: growl.rb 48 2007-11-18 21:47:29Z tim_pease $
2
+
3
+ require 'logging/appender'
4
+
5
+ module Logging
6
+ module Appenders
7
+
8
+ # This class provides an Appender that can send notifications to the Growl
9
+ # notification system on Mac OS X.
10
+ #
11
+ # +growlnotify+ must be installed somewhere in the path in order for the
12
+ # appender to function properly.
13
+ #
14
+ class Growl < ::Logging::Appender
15
+
16
+ # call-seq:
17
+ # Growl.new( name, opts = {} )
18
+ #
19
+ # Create an appender that will log messages to the Growl framework on a
20
+ # Mac OS X machine.
21
+ #
22
+ def initialize( name, opts = {} )
23
+ super
24
+
25
+ @growl = "growlnotify -n '#{@name}' -t '%s' -m '%s' -p %d"
26
+
27
+ getopt = ::Logging.options(opts)
28
+ @coalesce = getopt[:coalesce, false]
29
+ @title_sep = getopt[:separator]
30
+
31
+ # provides a mapping from the default Logging levels
32
+ # to the Growl notification levels
33
+ @map = [-2, -1, 0, 1, 2]
34
+
35
+ map = getopt[:map]
36
+ self.map = map unless map.nil?
37
+
38
+ setup_coalescing if @coalesce
39
+ end
40
+
41
+ # call-seq:
42
+ # map = { logging_levels => growl_levels }
43
+ #
44
+ # Configure the mapping from the Logging levels to the Growl
45
+ # notification levels. This is needed in order to log events at the
46
+ # proper Growl level.
47
+ #
48
+ # Without any configuration, the following maping will be used:
49
+ #
50
+ # :debug => -2
51
+ # :info => -1
52
+ # :warn => 0
53
+ # :error => 1
54
+ # :fatal => 2
55
+ #
56
+ def map=( levels )
57
+ map = []
58
+ levels.keys.each do |lvl|
59
+ num = ::Logging.level_num(lvl)
60
+ map[num] = growl_level_num(levels[lvl])
61
+ end
62
+ @map = map
63
+ end
64
+
65
+ # call-seq:
66
+ # append( event )
67
+ #
68
+ # Send the given _event_ to the Growl framework. The log event will be
69
+ # processed through the Layout assciated with this appender. The message
70
+ # will be logged at the level specified by the event.
71
+ #
72
+ def append( event )
73
+ if closed?
74
+ raise RuntimeError,
75
+ "appender '<#{self.class.name}: #{@name}>' is closed"
76
+ end
77
+
78
+ sync do
79
+ title = ''
80
+ message = @layout.format(event)
81
+ priority = @map[event.level]
82
+
83
+ if @title_sep
84
+ title, message = message.split(@title_sep)
85
+ title, message = message, title if message.nil?
86
+ end
87
+
88
+ growl(title, message, priority)
89
+ end unless @level > event.level
90
+ self
91
+ end
92
+
93
+ # call-seq:
94
+ # syslog << string
95
+ #
96
+ # Write the given _string_ to the Growl framework "as is" -- no
97
+ # layout formatting will be performed. The string will be logged at the
98
+ # 0 notification level of the Growl framework.
99
+ #
100
+ def <<( str )
101
+ if closed?
102
+ raise RuntimeError,
103
+ "appender '<#{self.class.name}: #{@name}>' is closed"
104
+ end
105
+
106
+ title = ''
107
+ message = str
108
+
109
+ if @title_sep
110
+ title, message = message.split(@title_sep)
111
+ title, message = message, title if message.nil?
112
+ end
113
+
114
+ sync {growl(title, message, 0)}
115
+ self
116
+ end
117
+
118
+
119
+ private
120
+
121
+ # call-seq:
122
+ # growl_level_num( level ) => integer
123
+ #
124
+ # Takes the given _level_ as a string or integer and returns the
125
+ # corresponding Growl notification level number.
126
+ #
127
+ def growl_level_num( level )
128
+ level = case level
129
+ when Integer: level
130
+ when String: Integer(level)
131
+ else raise ArgumentError, "unkonwn level '#{level}'" end
132
+ if level < -2 or level > 2
133
+ raise ArgumentError, "level '#{level}' is not in range -2..2"
134
+ end
135
+ level
136
+ end
137
+
138
+ # call-seq:
139
+ # growl( title, message, priority )
140
+ #
141
+ # Send the _message_ to the growl notifier using the given _title_ and
142
+ # _priority_.
143
+ #
144
+ def growl( title, message, priority )
145
+ if @coalesce then coalesce(title, message, priority)
146
+ else system @growl % [title, message, priority] end
147
+ end
148
+
149
+ # call-seq:
150
+ # coalesce( title, message, priority )
151
+ #
152
+ # Attempt to coalesce the given _message_ with any that might be pending
153
+ # in the queue to send to the growl notifier. Messages are coalesced
154
+ # with any in the queue that have the same _title_ and _priority_.
155
+ #
156
+ # There can be only one message in the queue, so if the _title_ and/or
157
+ # _priority_ don't match, the message in the queue is sent immediately
158
+ # to the growl notifier, and the current _message_ is queued.
159
+ #
160
+ def coalesce( *msg )
161
+ @c_mutex.synchronize do
162
+ if @c_message.nil? or @c_message.first != msg.first or @c_message.last != msg.last
163
+ @c_message, msg = msg, @c_message
164
+ @c_thread.run
165
+
166
+ else
167
+ @c_message[1] << "\n" << msg[1]
168
+ msg = nil
169
+ end
170
+ end
171
+
172
+ system @growl % msg unless msg.nil?
173
+ Thread.pass
174
+ end
175
+
176
+ # call-seq:
177
+ # setup_coalescing
178
+ #
179
+ # Setup the appender to handle coalescing of messages before sending
180
+ # them to the growl notifier. This requires the creation of a thread and
181
+ # mutex for passing messages from the appender thread to the growl
182
+ # notifier thread.
183
+ #
184
+ def setup_coalescing
185
+ @c_mutex = Mutex.new
186
+ @c_message = nil
187
+
188
+ @c_thread = Thread.new do
189
+ loop do
190
+ Thread.stop
191
+ sleep 0.5
192
+ @c_mutex.synchronize do
193
+ break if @c_message.nil?
194
+ system @growl % @c_message
195
+ @c_message = nil
196
+ end
197
+ end # loop
198
+ end # Thread.new
199
+ end
200
+
201
+ end # class Growl
202
+
203
+ end # module Appenders
204
+ end # module Logging
205
+
206
+ # EOF
@@ -1,17 +1,15 @@
1
- # $Id: io.rb 32 2007-02-22 23:32:17Z tim_pease $
1
+ # $Id: io.rb 37 2007-10-26 19:12:44Z tim_pease $
2
2
 
3
3
  require 'logging/appender'
4
4
 
5
5
  module Logging
6
6
  module Appenders
7
7
 
8
- #
9
8
  # This class provides an Appender that can write to any IO stream
10
9
  # configured for writing.
11
10
  #
12
11
  class IO < ::Logging::Appender
13
12
 
14
- #
15
13
  # call-seq:
16
14
  # IO.new( name, io )
17
15
  # IO.new( name, io, :layout => layout )
@@ -29,7 +27,6 @@ module Appenders
29
27
  super(name, opts)
30
28
  end
31
29
 
32
- #
33
30
  # call-seq:
34
31
  # close( footer = true )
35
32
  #
@@ -46,7 +43,6 @@ module Appenders
46
43
  self
47
44
  end
48
45
 
49
- #
50
46
  # call-seq:
51
47
  # flush
52
48
  #
@@ -61,7 +57,7 @@ module Appenders
61
57
 
62
58
 
63
59
  private
64
- #
60
+
65
61
  # call-seq:
66
62
  # write( str )
67
63
  #
@@ -1,10 +1,9 @@
1
- # $Id: rolling_file.rb 31 2007-02-22 23:16:17Z tim_pease $
1
+ # $Id: rolling_file.rb 37 2007-10-26 19:12:44Z tim_pease $
2
2
 
3
3
  require 'logging/appenders/file'
4
4
 
5
5
  module Logging::Appenders
6
6
 
7
- #
8
7
  # An appender that writes to a file and ensures that the file size or age
9
8
  # never exceeds some user specified level.
10
9
  #
@@ -31,7 +30,6 @@ module Logging::Appenders
31
30
  #
32
31
  class RollingFile < ::Logging::Appenders::File
33
32
 
34
- #
35
33
  # call-seq:
36
34
  # RollingFile.new( name, opts )
37
35
  #
@@ -139,8 +137,9 @@ module Logging::Appenders
139
137
  super(name, opts)
140
138
  end
141
139
 
140
+
142
141
  private
143
- #
142
+
144
143
  # call-seq:
145
144
  # write( str )
146
145
  #
@@ -154,7 +153,6 @@ module Logging::Appenders
154
153
  roll if roll_required? # the file IO stream is probably not being
155
154
  end # flushed to disk immediately
156
155
 
157
- #
158
156
  # call-seq:
159
157
  # roll
160
158
  #
@@ -167,7 +165,6 @@ module Logging::Appenders
167
165
  @io = ::File.new(@fn, 'a')
168
166
  end
169
167
 
170
- #
171
168
  # call-seq:
172
169
  # roll_required?
173
170
  #
@@ -184,7 +181,6 @@ module Logging::Appenders
184
181
  return sufficiently_aged?
185
182
  end
186
183
 
187
- #
188
184
  # call-seq:
189
185
  # roll_files
190
186
  #
@@ -1,4 +1,4 @@
1
- # $Id: static_appender.rb 26 2007-01-31 23:19:40Z tim_pease $
1
+ # $Id: static_appender.rb 37 2007-10-26 19:12:44Z tim_pease $
2
2
 
3
3
  require 'logging/appenders/console'
4
4
 
@@ -9,7 +9,6 @@ class Appender
9
9
 
10
10
  class << self
11
11
 
12
- #
13
12
  # call-seq:
14
13
  # Appender[name]
15
14
  #
@@ -18,7 +17,6 @@ class Appender
18
17
  #
19
18
  def []( name ) @appenders[name] end
20
19
 
21
- #
22
20
  # call-seq:
23
21
  # Appender[name] = appender
24
22
  #
@@ -27,7 +25,6 @@ class Appender
27
25
  #
28
26
  def []=( name, val ) @appenders[name] = val end
29
27
 
30
- #
31
28
  # call-seq:
32
29
  # Appender.stdout
33
30
  #
@@ -39,7 +36,6 @@ class Appender
39
36
  #
40
37
  def stdout( ) self['stdout'] || ::Logging::Appenders::Stdout.new end
41
38
 
42
- #
43
39
  # call-seq:
44
40
  # Appender.stderr
45
41
  #
@@ -0,0 +1,210 @@
1
+ # $Id: syslog.rb 46 2007-10-31 14:39:01Z tim_pease $
2
+
3
+ require 'logging/appender'
4
+ require 'syslog'
5
+
6
+ module Logging
7
+ module 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
+ super
90
+
91
+ ident = opts[:ident] || opts['ident'] || name
92
+ logopt = opts[:logopt] || opts['logopt'] || (LOG_PID | LOG_CONS)
93
+ facility = opts[:facility] || opts['facility'] || LOG_USER
94
+ @syslog = ::Syslog.open(ident, Integer(logopt), Integer(facility))
95
+
96
+ # provides a mapping from the default Logging levels
97
+ # to the syslog levels
98
+ @map = [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT]
99
+
100
+ if opts.has_key?('map') or opts.has_key?(:map)
101
+ self.map = opts[:map] || opts['map']
102
+ end
103
+ end
104
+
105
+ # call-seq:
106
+ # map = { logging_levels => syslog_levels }
107
+ #
108
+ # Configure the mapping from the Logging levels to the syslog levels.
109
+ # This is needed in order to log events at the proper syslog level.
110
+ #
111
+ # Without any configuration, the following maping will be used:
112
+ #
113
+ # :debug => LOG_DEBUG
114
+ # :info => LOG_INFO
115
+ # :warn => LOG_WARNING
116
+ # :error => LOG_ERR
117
+ # :fatal => LOG_CRIT
118
+ #
119
+ def map=( levels )
120
+ map = []
121
+ levels.keys.each do |lvl|
122
+ num = ::Logging.level_num(lvl)
123
+ map[num] = syslog_level_num(levels[lvl])
124
+ end
125
+ @map = map
126
+ end
127
+
128
+ # call-seq:
129
+ # close
130
+ #
131
+ # Closes the connetion to the syslog facility.
132
+ #
133
+ def close
134
+ @syslog.close
135
+ end
136
+
137
+ # call-seq:
138
+ # closed? => true or false
139
+ #
140
+ # Queries the connection to the syslog facility and returns +true+ if
141
+ # the connection is closed.
142
+ #
143
+ def closed?
144
+ !@syslog.opened?
145
+ end
146
+
147
+ # call-seq:
148
+ # append( event )
149
+ #
150
+ # Write the given _event_ to the syslog facility. The log event will be
151
+ # processed through the Layout assciated with this appender. The message
152
+ # will be logged at the level specified by the event.
153
+ #
154
+ def append( event )
155
+ if closed?
156
+ raise RuntimeError,
157
+ "appender '<#{self.class.name}: #{@name}>' is closed"
158
+ end
159
+
160
+ sync do
161
+ msg = @layout.format(event)
162
+ pri = @map[event.level]
163
+ @syslog.log(pri, '%s', msg)
164
+ end unless @level > event.level
165
+ self
166
+ end
167
+
168
+ # call-seq:
169
+ # syslog << string
170
+ #
171
+ # Write the given _string_ to the syslog facility "as is" -- no
172
+ # layout formatting will be performed. The string will be logged at the
173
+ # LOG_DEBUG level of the syslog facility.
174
+ #
175
+ def <<( str )
176
+ if closed?
177
+ raise RuntimeError,
178
+ "appender '<#{self.class.name}: #{@name}>' is closed"
179
+ end
180
+
181
+ sync {@syslog.log(LOG_DEBUG, '%s', str)}
182
+ self
183
+ end
184
+
185
+
186
+ private
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 Appenders
208
+ end # module Logging
209
+
210
+ # EOF