logging 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +6 -0
  3. data/Rakefile +1 -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/simple_logging.rb +13 -0
  8. data/lib/logging.rb +14 -8
  9. data/lib/logging/appender.rb +36 -6
  10. data/lib/logging/appenders/console.rb +0 -1
  11. data/lib/logging/appenders/email.rb +3 -4
  12. data/lib/logging/appenders/file.rb +0 -1
  13. data/lib/logging/appenders/growl.rb +16 -5
  14. data/lib/logging/appenders/io.rb +0 -1
  15. data/lib/logging/appenders/rolling_file.rb +1 -2
  16. data/lib/logging/appenders/syslog.rb +0 -1
  17. data/lib/logging/config/configurator.rb +182 -0
  18. data/lib/logging/config/yaml_configurator.rb +0 -1
  19. data/lib/logging/layout.rb +0 -1
  20. data/lib/logging/layouts/basic.rb +0 -1
  21. data/lib/logging/layouts/pattern.rb +5 -6
  22. data/lib/logging/log_event.rb +0 -1
  23. data/lib/logging/logger.rb +24 -14
  24. data/lib/logging/repository.rb +13 -3
  25. data/lib/logging/root_logger.rb +0 -1
  26. data/lib/logging/utils.rb +0 -1
  27. data/tasks/ann.rake +0 -1
  28. data/tasks/bones.rake +0 -1
  29. data/tasks/gem.rake +0 -1
  30. data/tasks/git.rake +1 -2
  31. data/tasks/manifest.rake +0 -1
  32. data/tasks/notes.rake +0 -1
  33. data/tasks/post_load.rake +0 -1
  34. data/tasks/rdoc.rake +0 -1
  35. data/tasks/rubyforge.rake +1 -2
  36. data/tasks/setup.rb +0 -1
  37. data/tasks/test.rake +0 -1
  38. data/test/appenders/test_console.rb +0 -1
  39. data/test/appenders/test_email.rb +0 -1
  40. data/test/appenders/test_file.rb +0 -1
  41. data/test/appenders/test_growl.rb +22 -9
  42. data/test/appenders/test_io.rb +30 -3
  43. data/test/appenders/test_rolling_file.rb +0 -1
  44. data/test/appenders/test_syslog.rb +0 -1
  45. data/test/benchmark.rb +0 -1
  46. data/test/config/test_configurator.rb +70 -0
  47. data/test/config/test_yaml_configurator.rb +0 -1
  48. data/test/layouts/test_basic.rb +0 -1
  49. data/test/layouts/test_pattern.rb +0 -1
  50. data/test/setup.rb +0 -1
  51. data/test/test_appender.rb +0 -1
  52. data/test/test_layout.rb +0 -1
  53. data/test/test_log_event.rb +0 -1
  54. data/test/test_logger.rb +2 -2
  55. data/test/test_logging.rb +1 -2
  56. data/test/test_repository.rb +2 -3
  57. data/test/test_root_logger.rb +0 -1
  58. data/test/test_utils.rb +0 -1
  59. metadata +9 -2
@@ -1,3 +1,12 @@
1
+ == 0.9.0 / 2008-07-
2
+
3
+ 2 minor enhancement
4
+ - Exceptions from appenders are captured and logged
5
+ - Internal logger for the Logging framework (disabled by default)
6
+ - Added a DSL configuration format (more readable than YAML)
7
+ 1 bug fix
8
+ - Modules could not have their own logger instance
9
+
1
10
  == 0.8.0 / 2008-07-02
2
11
 
3
12
  1 minor enhancement
@@ -2,7 +2,11 @@ History.txt
2
2
  Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
+ data/bad_logging_1.rb
6
+ data/bad_logging_2.rb
7
+ data/logging.rb
5
8
  data/logging.yaml
9
+ data/simple_logging.rb
6
10
  lib/logging.rb
7
11
  lib/logging/appender.rb
8
12
  lib/logging/appenders/console.rb
@@ -12,6 +16,7 @@ lib/logging/appenders/growl.rb
12
16
  lib/logging/appenders/io.rb
13
17
  lib/logging/appenders/rolling_file.rb
14
18
  lib/logging/appenders/syslog.rb
19
+ lib/logging/config/configurator.rb
15
20
  lib/logging/config/yaml_configurator.rb
16
21
  lib/logging/layout.rb
17
22
  lib/logging/layouts/basic.rb
@@ -40,6 +45,7 @@ test/appenders/test_io.rb
40
45
  test/appenders/test_rolling_file.rb
41
46
  test/appenders/test_syslog.rb
42
47
  test/benchmark.rb
48
+ test/config/test_configurator.rb
43
49
  test/config/test_yaml_configurator.rb
44
50
  test/layouts/test_basic.rb
45
51
  test/layouts/test_pattern.rb
data/Rakefile CHANGED
@@ -17,6 +17,7 @@ PROJ.rdoc.dir = 'doc/rdoc'
17
17
  #PROJ.rdoc.remote_dir = 'rdoc'
18
18
  PROJ.rdoc.remote_dir = ''
19
19
  PROJ.version = Logging::VERSION
20
+ PROJ.release_name = 'Monkeyful Chimptacular'
20
21
 
21
22
  PROJ.exclude << %w[^tags$ ^tasks/archive ^coverage]
22
23
  PROJ.rdoc.exclude << '^data'
@@ -0,0 +1,13 @@
1
+
2
+ Logging.configure {
3
+
4
+ logger(:root) {
5
+ level :info
6
+ appenders 'bad'
7
+ }
8
+
9
+ appender('bad') {
10
+ type 'FooBar'
11
+ }
12
+
13
+ } # logging configuration
@@ -0,0 +1,21 @@
1
+
2
+ Logging.configure {
3
+
4
+ logger(:root) {
5
+ level :info
6
+ appenders 'logfile'
7
+ }
8
+
9
+ appender('logfile') {
10
+ type 'File'
11
+ level 'DEB'
12
+ filename 'tmp/temp.log'
13
+ truncate true
14
+ layout {
15
+ type 'BadLayout'
16
+ date_method 'to_s'
17
+ pattern '[%d] %l %c : %m\n'
18
+ }
19
+ }
20
+
21
+ } # logging configuration
@@ -0,0 +1,42 @@
1
+
2
+ Logging.configure {
3
+
4
+ pre_config {
5
+ levels %w[DEB INF PRT WRN ERR FAT]
6
+ format_as :inspect
7
+ }
8
+
9
+ logger('A::B::C') {
10
+ level 'DEB'
11
+ additive false
12
+ trace false
13
+ appenders %w[stderr logfile]
14
+ }
15
+
16
+ logger('yourlogger') {
17
+ level 'INF'
18
+ appenders %w[stderr logfile]
19
+ }
20
+
21
+ appender('stderr') {
22
+ type 'Stderr'
23
+ level 'DEB'
24
+ layout {
25
+ type 'Basic'
26
+ format_as :string
27
+ }
28
+ }
29
+
30
+ appender('logfile') {
31
+ type 'File'
32
+ level 'DEB'
33
+ filename 'tmp/temp.log'
34
+ truncate true
35
+ layout {
36
+ type 'Pattern'
37
+ date_method 'to_s'
38
+ pattern '[%d] %l %c : %m\n'
39
+ }
40
+ }
41
+
42
+ } # logging configuration
@@ -0,0 +1,13 @@
1
+
2
+ Logging.configure {
3
+
4
+ logger(:root) {
5
+ level :info
6
+ appenders 'stdout'
7
+ }
8
+
9
+ appender('stdout') {
10
+ type 'Stdout'
11
+ }
12
+
13
+ } # logging configuration
@@ -1,10 +1,8 @@
1
- # $Id$
2
1
 
3
2
  # Equivalent to a header guard in C/C++
4
3
  # Used to prevent the class/module from being loaded more than once
5
4
  unless defined? Logging
6
5
 
7
- # TODO: internal logger for debugging
8
6
  # TODO: Windows Log Service appender
9
7
 
10
8
  #
@@ -12,7 +10,7 @@ unless defined? Logging
12
10
  module Logging
13
11
 
14
12
  # :stopdoc:
15
- VERSION = '0.8.0'
13
+ VERSION = '0.9.0'
16
14
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
17
15
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
18
16
  WIN32 = %r/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
@@ -24,12 +22,20 @@ module Logging
24
22
 
25
23
  # call-seq:
26
24
  # Logging.configure( filename )
25
+ # Logging.configure { block }
27
26
  #
28
27
  # Configures the Logging framework using the configuration information
29
28
  # found in the given file. The file extension should be either '.yaml'
30
29
  # or '.yml' (XML configuration is not yet supported).
31
30
  #
32
- def configure( filename, *args )
31
+ def configure( *args, &block )
32
+ if block
33
+ return ::Logging::Config::Configurator.process(&block)
34
+ end
35
+
36
+ filename = args.shift
37
+ raise ArgumentError, 'a filename was not given' if filename.nil?
38
+
33
39
  case File.extname(filename)
34
40
  when '.yaml', '.yml'
35
41
  ::Logging::Config::YamlConfigurator.load(filename, *args)
@@ -187,7 +193,7 @@ module Logging
187
193
  end
188
194
 
189
195
  longest = names.values.inject {|x,y| (x.length > y.length) ? x : y}
190
- module_eval "MAX_LEVEL_LENGTH = #{longest.length}"
196
+ module_eval "MAX_LEVEL_LENGTH = #{longest.length}", __FILE__, __LINE__
191
197
 
192
198
  levels.keys
193
199
  end
@@ -214,7 +220,7 @@ module Logging
214
220
  raise ArgumentError, "unknown object format '#{f}'"
215
221
  end
216
222
 
217
- module_eval "OBJ_FORMAT = :#{f}"
223
+ module_eval "OBJ_FORMAT = :#{f}", __FILE__, __LINE__
218
224
  end
219
225
 
220
226
  # Returns the version string for the library.
@@ -228,7 +234,7 @@ module Logging
228
234
  # <tt>File.join</tt>.
229
235
  #
230
236
  def libpath( *args )
231
- args.empty? ? LIBPATH : ::File.join(LIBPATH, *args)
237
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
232
238
  end
233
239
 
234
240
  # Returns the lpath for the module. If any arguments are given,
@@ -236,7 +242,7 @@ module Logging
236
242
  # <tt>File.join</tt>.
237
243
  #
238
244
  def path( *args )
239
- args.empty? ? PATH : ::File.join(PATH, *args)
245
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
240
246
  end
241
247
 
242
248
  # Utility method used to rquire all files ending in .rb that lie in the
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  require 'thread'
4
3
 
@@ -71,7 +70,8 @@ class Appender
71
70
 
72
71
  end # class << self
73
72
 
74
- attr_reader :name, :layout, :level
73
+ attr_reader :name, :layout, :level, :log
74
+ private :log
75
75
 
76
76
  # call-seq:
77
77
  # Appender.new( name )
@@ -83,6 +83,8 @@ class Appender
83
83
  # created.
84
84
  #
85
85
  def initialize( name, opts = {} )
86
+ @log = ::Logging::Logger[self]
87
+
86
88
  @name = name.to_s
87
89
  @closed = false
88
90
 
@@ -91,7 +93,14 @@ class Appender
91
93
 
92
94
  @mutex = Mutex.new
93
95
  header = @layout.header
94
- sync {write(header)} unless header.nil? || header.empty?
96
+
97
+ unless header.nil? || header.empty?
98
+ begin
99
+ sync {write(header)}
100
+ rescue StandardError => err
101
+ log.error err
102
+ end
103
+ end
95
104
 
96
105
  ::Logging::Appender[@name] = self
97
106
  end
@@ -108,7 +117,16 @@ class Appender
108
117
  "appender '<#{self.class.name}: #{@name}>' is closed"
109
118
  end
110
119
 
111
- sync {write(event)} unless @level > event.level
120
+ # only append if the event level is less than or equal to the configured
121
+ # appender level
122
+ unless @level > event.level
123
+ begin
124
+ sync {write(event)}
125
+ rescue StandardError => err
126
+ log.error err
127
+ end
128
+ end
129
+
112
130
  self
113
131
  end
114
132
 
@@ -124,7 +142,13 @@ class Appender
124
142
  "appender '<#{self.class.name}: #{@name}>' is closed"
125
143
  end
126
144
 
127
- sync {write(str)} unless @level >= ::Logging::LEVELS.length
145
+ unless @level >= ::Logging::LEVELS.length
146
+ begin
147
+ sync {write(str)}
148
+ rescue StandardError => err
149
+ log.error err
150
+ end
151
+ end
128
152
  self
129
153
  end
130
154
 
@@ -197,7 +221,13 @@ class Appender
197
221
  @closed = true
198
222
  if footer
199
223
  footer = @layout.footer
200
- sync {write(footer)} unless footer.nil? || footer.empty?
224
+ unless footer.nil? || footer.empty?
225
+ begin
226
+ sync {write(footer)}
227
+ rescue StandardError => err
228
+ log.error err
229
+ end
230
+ end
201
231
  end
202
232
  self
203
233
  end
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  require Logging.libpath(*%w[logging appenders io])
4
3
 
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  require 'net/smtp'
4
3
  require 'time' # get rfc822 time format
@@ -116,10 +115,10 @@ class Email < ::Logging::Appender
116
115
  ### send email
117
116
  begin
118
117
  Net::SMTP.start(*@params) {|smtp| smtp.sendmail(rfc822msg, @from, @to)}
119
- rescue Exception => e
118
+ rescue StandardError => err
120
119
  self.level = :off
121
- STDERR.puts e.message
122
- # TODO - log that e-mail notification has been turned off
120
+ log.warn 'e-mail notifications have been disabled'
121
+ log.error err
123
122
  ensure
124
123
  @buff.clear
125
124
  end
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  module Logging::Appenders
4
3
 
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  module Logging::Appenders
4
3
 
@@ -35,7 +34,7 @@ module Logging::Appenders
35
34
  # make sure the growlnotify command can be called
36
35
  unless system('growlnotify -v 2>&1 > /dev/null')
37
36
  self.level = :off
38
- # TODO - log that the growl notification is turned off
37
+ log.warn 'growl notifications have been disabled'
39
38
  end
40
39
  end
41
40
 
@@ -118,7 +117,7 @@ module Logging::Appenders
118
117
  def growl( title, message, priority )
119
118
  message.tr!("`", "'")
120
119
  if @coalesce then coalesce(title, message, priority)
121
- else system @growl % [title, message, priority] end
120
+ else call_growl(title, message, priority) end
122
121
  end
123
122
 
124
123
  # call-seq:
@@ -168,15 +167,27 @@ module Logging::Appenders
168
167
  loop do
169
168
  sleep 0.5
170
169
  @c_mutex.synchronize {
171
- system(@growl % @c_queue.shift) until @c_queue.empty?
170
+ call_growl(*@c_queue.shift) until @c_queue.empty?
172
171
  }
173
172
  Thread.stop if @c_queue.empty?
174
173
  end # loop
175
174
  end # Thread.new
176
175
  end
177
176
 
178
- end # class Growl
177
+ # call-seq:
178
+ # call_growl( title, message, priority )
179
+ #
180
+ # Call the growlnotify application with the given parameters. If the
181
+ # system call fails, the growl appender will be disabled.
182
+ #
183
+ def call_growl( *args )
184
+ unless system(@growl % args)
185
+ self.level = :off
186
+ log.warn 'growl notifications have been disabled'
187
+ end
188
+ end
179
189
 
190
+ end # class Growl
180
191
  end # module Logging::Appenders
181
192
 
182
193
  # EOF
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  module Logging::Appenders
4
3
 
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  require 'lockfile'
4
3
 
@@ -133,7 +132,7 @@ module Logging::Appenders
133
132
  CODE
134
133
  end
135
134
  meta = class << self; self end
136
- meta.class_eval code
135
+ meta.class_eval code, __FILE__, __LINE__
137
136
 
138
137
  # if the truncate flag was set to true, then roll
139
138
  roll_now = opts.getopt(:truncate, false)
@@ -1,4 +1,3 @@
1
- # $Id$
2
1
 
3
2
  begin
4
3
  require 'syslog'
@@ -0,0 +1,182 @@
1
+
2
+ module Logging::Config
3
+
4
+ # The Configurator class is used to configure the Logging framework
5
+ # using information found in a block of Ruby code. This block is evaluated
6
+ # in the context of the configurator's DSL.
7
+ #
8
+ class Configurator
9
+
10
+ class Error < StandardError; end # :nodoc:
11
+
12
+ # call-seq:
13
+ # Configuraotr.process( &block )
14
+ #
15
+ def self.process( &block )
16
+ new.load(&block)
17
+ end
18
+
19
+ # call-seq:
20
+ # load { block }
21
+ #
22
+ # Loads the configuration from the _block_ and configures the Logging
23
+ # gem.
24
+ #
25
+ def load( &block )
26
+ raise Error, "missing configuration block" unless block
27
+
28
+ dsl = TopLevelDSL.new
29
+ dsl.__instance_eval(&block)
30
+
31
+ pre_config dsl.__pre_config
32
+ appenders dsl.__appenders
33
+ loggers dsl.__loggers
34
+ end
35
+
36
+ # call-seq:
37
+ # pre_config( config )
38
+ #
39
+ # Configures the logging levels, object format style, and root logging
40
+ # level.
41
+ #
42
+ def pre_config( config )
43
+ if config.nil?
44
+ ::Logging.init unless ::Logging.const_defined? 'MAX_LEVEL_LENGTH'
45
+ return
46
+ end
47
+
48
+ # define levels
49
+ levels = config[:levels]
50
+ ::Logging.init(levels) unless levels.nil?
51
+
52
+ # format as
53
+ format = config[:format_as]
54
+ ::Logging.format_as(format) unless format.nil?
55
+ end
56
+
57
+ # call-seq:
58
+ # appenders( ary )
59
+ #
60
+ # Given an array of Appender configurations, this method will iterate
61
+ # over each and create the Appender(s).
62
+ #
63
+ def appenders( ary )
64
+ ary.each {|name, config| appender(name, config)}
65
+ end
66
+
67
+ # call-seq:
68
+ # loggers( ary )
69
+ #
70
+ # Given an array of Logger configurations, this method will iterate over
71
+ # each and create the Logger(s).
72
+ #
73
+ def loggers( ary )
74
+ ary.each do |name, config|
75
+ l = Logging::Logger[name]
76
+ l.level = config[:level] if config[:level]
77
+ l.additive = config[:additive] if l.respond_to? :additive=
78
+ l.trace = config[:trace]
79
+ l.appenders = Array(config[:appenders]).
80
+ map {|name| ::Logging::Appender[name]}
81
+ end
82
+ end
83
+
84
+ # call-seq:
85
+ # appender( name, config )
86
+ #
87
+ # Creates a new Appender based on the given _config_ options (a hash).
88
+ # The type of Appender created is determined by the 'type' option in the
89
+ # config. The remaining config options are passed to the Appender
90
+ # initializer.
91
+ #
92
+ # The config options can also contain a 'layout' option. This should be
93
+ # another set of options used to create a Layout for this Appender.
94
+ #
95
+ def appender( name, config )
96
+ type = config.delete(:type)
97
+ raise Error, "appender type not given for #{name.inspect}" if type.nil?
98
+
99
+ config[:layout] = layout(config[:layout]) if config.has_key? :layout
100
+
101
+ clazz = ::Logging::Appenders.const_get type
102
+ clazz.new(name, config)
103
+ rescue NameError => err
104
+ raise Error, "unknown appender class Logging::Appenders::#{type}"
105
+ end
106
+
107
+ # call-seq:
108
+ # layout( config )
109
+ #
110
+ # Creates a new Layout based on the given _config_ options (a hash).
111
+ # The type of Layout created is determined by the 'type' option in the
112
+ # config. The remaining config options are passed to the Layout
113
+ # initializer.
114
+ #
115
+ def layout( config )
116
+ return ::Logging::Layouts::Basic.new if config.nil?
117
+
118
+ type = config.delete(:type)
119
+ raise Error, 'layout type not given' if type.nil?
120
+
121
+ clazz = ::Logging::Layouts.const_get type
122
+ clazz.new config
123
+ rescue NameError => err
124
+ raise Error, "unknown layout class Logging::Layouts::#{type}"
125
+ end
126
+
127
+ class DSL
128
+ alias :__instance_eval :instance_eval
129
+
130
+ instance_methods.each do |m|
131
+ undef_method m unless m[%r/^__/]
132
+ end
133
+
134
+ def self.process( &block )
135
+ dsl = new
136
+ dsl.__instance_eval(&block)
137
+ dsl.__hash
138
+ end
139
+
140
+ def __hash
141
+ @hash ||= Hash.new
142
+ end
143
+
144
+ def method_missing( method, *args, &block )
145
+ args << DSL.process(&block) if block
146
+
147
+ key = method.to_sym
148
+ value = (1 == args.length ? args.first : args)
149
+ __store(key, value)
150
+ end
151
+
152
+ def __store( key, value )
153
+ __hash[key] = value
154
+ end
155
+ end
156
+
157
+ class TopLevelDSL < DSL
158
+ undef_method :method_missing
159
+
160
+ def pre_config( &block )
161
+ __store(:preconfig, DSL.process(&block))
162
+ end
163
+
164
+ def logger( name, &block )
165
+ @loggers ||= []
166
+ @loggers << [name, DSL.process(&block)]
167
+ end
168
+
169
+ def appender( name, &block )
170
+ @appenders ||= []
171
+ @appenders << [name, DSL.process(&block)]
172
+ end
173
+
174
+ def __pre_config() __hash[:preconfig]; end
175
+ def __loggers() @loggers; end
176
+ def __appenders() @appenders; end
177
+ end
178
+
179
+ end # class Configurator
180
+ end # module Logging::Config
181
+
182
+ # EOF