logging 0.9.8 → 1.0.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.
Files changed (46) hide show
  1. data/History.txt +13 -0
  2. data/README.rdoc +51 -39
  3. data/Rakefile +1 -0
  4. data/examples/appenders.rb +44 -0
  5. data/examples/classes.rb +39 -0
  6. data/examples/formatting.rb +49 -0
  7. data/examples/hierarchies.rb +71 -0
  8. data/examples/layouts.rb +45 -0
  9. data/examples/loggers.rb +26 -0
  10. data/examples/names.rb +40 -0
  11. data/examples/simple.rb +14 -0
  12. data/lib/logging.rb +50 -21
  13. data/lib/logging/appender.rb +4 -56
  14. data/lib/logging/appenders.rb +120 -0
  15. data/lib/logging/appenders/buffering.rb +2 -1
  16. data/lib/logging/appenders/console.rb +0 -2
  17. data/lib/logging/appenders/rolling_file.rb +0 -2
  18. data/lib/logging/appenders/string_io.rb +1 -3
  19. data/lib/logging/appenders/syslog.rb +0 -7
  20. data/lib/logging/config/configurator.rb +1 -1
  21. data/lib/logging/config/yaml_configurator.rb +3 -7
  22. data/lib/logging/layout.rb +2 -4
  23. data/lib/logging/layouts.rb +47 -0
  24. data/lib/logging/layouts/basic.rb +2 -4
  25. data/lib/logging/layouts/parseable.rb +211 -0
  26. data/lib/logging/layouts/pattern.rb +6 -8
  27. data/lib/logging/log_event.rb +1 -1
  28. data/lib/logging/logger.rb +4 -4
  29. data/lib/spec/logging_helper.rb +2 -2
  30. data/test/appenders/test_buffered_io.rb +26 -18
  31. data/test/appenders/test_console.rb +10 -10
  32. data/test/appenders/test_email.rb +18 -19
  33. data/test/appenders/test_file.rb +12 -12
  34. data/test/appenders/test_growl.rb +11 -12
  35. data/test/appenders/test_io.rb +14 -15
  36. data/test/appenders/test_rolling_file.rb +15 -24
  37. data/test/appenders/test_syslog.rb +10 -10
  38. data/test/layouts/test_basic.rb +4 -5
  39. data/test/layouts/test_json.rb +112 -0
  40. data/test/layouts/test_pattern.rb +9 -9
  41. data/test/layouts/test_yaml.rb +121 -0
  42. data/test/setup.rb +1 -1
  43. data/test/test_appender.rb +0 -14
  44. data/test/test_log_event.rb +1 -1
  45. data/test/test_logging.rb +3 -3
  46. metadata +17 -2
@@ -1,6 +1,4 @@
1
1
 
2
- require 'yaml'
3
-
4
2
  module Logging
5
3
 
6
4
  # The +Layout+ class provides methods for formatting log events into a
@@ -30,6 +28,8 @@ class Layout
30
28
  # then <tt>:string</tt> is used.
31
29
  #
32
30
  def initialize( opts = {} )
31
+ ::Logging.init unless ::Logging.const_defined? :MAX_LEVEL_LENGTH
32
+
33
33
  default = ::Logging.const_defined?('OBJ_FORMAT') ?
34
34
  ::Logging::OBJ_FORMAT : nil
35
35
 
@@ -114,6 +114,4 @@ class Layout
114
114
  end # class Layout
115
115
  end # module Logging
116
116
 
117
- Logging.require_all_libs_relative_to(__FILE__, 'layouts')
118
-
119
117
  # EOF
@@ -0,0 +1,47 @@
1
+
2
+ module Logging
3
+ module Layouts
4
+
5
+ # Accessor / Factory for the Basic layout.
6
+ #
7
+ def basic( *args )
8
+ return ::Logging::Layouts::Basic if args.empty?
9
+ ::Logging::Layouts::Basic.new(*args)
10
+ end
11
+
12
+ # Accessor / Factory for the Pattern layout.
13
+ #
14
+ def pattern( *args )
15
+ return ::Logging::Layouts::Pattern if args.empty?
16
+ ::Logging::Layouts::Pattern.new(*args)
17
+ end
18
+
19
+ # Accessor for the Parseable layout.
20
+ #
21
+ def parseable
22
+ ::Logging::Layouts::Parseable
23
+ end
24
+
25
+ # Factory for the Parseable layout using JSON formatting.
26
+ #
27
+ def json( *args )
28
+ ::Logging::Layouts::Parseable.json(*args)
29
+ end
30
+
31
+ # Factory for the Parseable layout using YAML formatting.
32
+ #
33
+ def yaml( *args )
34
+ ::Logging::Layouts::Parseable.yaml(*args)
35
+ end
36
+
37
+ extend self
38
+ end # module Layouts
39
+ end # module Logging
40
+
41
+
42
+ %w[basic parseable pattern].
43
+ each do |fn|
44
+ require ::Logging.libpath('logging', 'layouts', fn)
45
+ end
46
+
47
+ # EOF
@@ -1,6 +1,5 @@
1
1
 
2
- module Logging
3
- module Layouts
2
+ module Logging::Layouts
4
3
 
5
4
  # The +Basic+ layout class provides methods for simple formatting of log
6
5
  # events. The resulting string follows the format below.
@@ -28,7 +27,6 @@ module Layouts
28
27
  end
29
28
 
30
29
  end # class Basic
31
- end # module Layouts
32
- end # module Logging
30
+ end # module Logging::Layouts
33
31
 
34
32
  # EOF
@@ -0,0 +1,211 @@
1
+
2
+ module Logging::Layouts
3
+
4
+ # This layout will produce parseable log output in either JSON or YAML
5
+ # format. This makes it much easier for machines to parse log files and
6
+ # perform analysis on those logs.
7
+ #
8
+ # The information about the log event can be configured when the layout is
9
+ # created. Any or all of the following labels can be set as the _items_ to
10
+ # log:
11
+ #
12
+ # 'logger' Used to output the name of the logger that generated the
13
+ # log event.
14
+ # 'timestamp' Used to output the timestamp of the log event.
15
+ # 'level' Used to output the level of the log event.
16
+ # 'message' Used to output the application supplied message
17
+ # associated with the log event.
18
+ # 'file' Used to output the file name where the logging request
19
+ # was issued.
20
+ # 'line' Used to output the line number where the logging request
21
+ # was issued.
22
+ # 'method' Used to output the method name where the logging request
23
+ # was issued.
24
+ # 'pid' Used to output the process ID of the currently running
25
+ # program.
26
+ # 'millis' Used to output the number of milliseconds elapsed from
27
+ # the construction of the Layout until creation of the log
28
+ # event.
29
+ # 'thread_id' Used to output the object ID of the thread that generated
30
+ # the log event.
31
+ # 'thread' Used to output the name of the thread that generated the
32
+ # log event. Name can be specified using Thread.current[:name]
33
+ # notation. Output empty string if name not specified. This
34
+ # option helps to create more human readable output for
35
+ # multithread application logs.
36
+ #
37
+ # These items are supplied to the layout as an array of strings. The items
38
+ # 'file', 'line', and 'method' will only work if the Logger generating the
39
+ # events is configured to generate tracing information. If this is not the
40
+ # case these fields will always be empty.
41
+ #
42
+ # When configured to output log events in YAML format, each log message
43
+ # will be formatted as a hash in it's own YAML document. The hash keys are
44
+ # the name of the item, and the value is what you would expect it to be.
45
+ # Therefore, for the default set of times log message would appear as
46
+ # follows:
47
+ #
48
+ # ---
49
+ # timestamp: 2009-04-17 16:15:42
50
+ # level: INFO
51
+ # logger: Foo::Bar
52
+ # message: this is a log message
53
+ # ---
54
+ # timestamp: 2009-04-17 16:15:43
55
+ # level: ERROR
56
+ # logger: Foo
57
+ # message: <RuntimeError> Oooops!!
58
+ #
59
+ # The output order of the fields is not guaranteed to be the same as the
60
+ # order specified in the _items_ list. This is because Ruby hashes are not
61
+ # ordered by default (unless your running this in Ruby 1.9).
62
+ #
63
+ # When configured to output log events in JSON format, each log message
64
+ # will be formatted as an object (in the JSON sense of the work) on it's
65
+ # own line in the log output. Therefore, to parse the output you must read
66
+ # it line by line and parse the individual objects. Taking the same
67
+ # example above the JSON output would be:
68
+ #
69
+ # {"timestamp":"2009-04-17 16:15:42","level":"INFO","logger":"Foo::Bar","message":"this is a log message"}
70
+ # {"timestamp":"2009-04-17 16:15:43","level":"ERROR","logger":"Foo","message":"<RuntimeError> Oooops!!"}
71
+ #
72
+ # The output order of the fields is guaranteed to be the same as the order
73
+ # specified in the _items_ list.
74
+ #
75
+ class Parseable < ::Logging::Layout
76
+
77
+ # :stopdoc:
78
+ # Arguments to sprintf keyed to directive letters
79
+ DIRECTIVE_TABLE = {
80
+ 'logger' => 'event.logger',
81
+ 'timestamp' => 'Time.now.strftime(Pattern::ISO8601)',
82
+ 'level' => '::Logging::LNAMES[event.level]',
83
+ 'message' => 'format_obj(event.data)',
84
+ 'file' => 'event.file',
85
+ 'line' => 'event.line',
86
+ 'method' => 'event.method',
87
+ 'pid' => 'Process.pid',
88
+ 'millis' => 'Integer((Time.now-@created_at)*1000)',
89
+ 'thread_id' => 'Thread.current.object_id',
90
+ 'thread' => 'Thread.current[:name]'
91
+ }
92
+
93
+ # call-seq:
94
+ # Pattern.create_yaml_format_methods( layout )
95
+ #
96
+ # This method will create the +format+ method in the given Parseable
97
+ # _layout_ based on the configured items for the layout instance.
98
+ #
99
+ def self.create_yaml_format_method( layout )
100
+ code = "undef :format if method_defined? :format\n"
101
+ code << "def format( event )\nstr = {\n"
102
+
103
+ code << layout.items.map {|name|
104
+ "'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}"
105
+ }.join(",\n")
106
+ code << "\n}.to_yaml\nreturn str\nend\n"
107
+
108
+ (class << layout; self end).class_eval(code, __FILE__, __LINE__)
109
+ end
110
+
111
+ # call-seq:
112
+ # Pattern.create_json_format_methods( layout )
113
+ #
114
+ # This method will create the +format+ method in the given Parseable
115
+ # _layout_ based on the configured items for the layout instance.
116
+ #
117
+ def self.create_json_format_method( layout )
118
+ code = "undef :format if method_defined? :format\n"
119
+ code << "def format( event )\n\"{"
120
+
121
+ args = []
122
+ code << layout.items.map {|name|
123
+ args << "format_as_json(#{Parseable::DIRECTIVE_TABLE[name]})"
124
+ "\\\"#{name}\\\":%s"
125
+ }.join(',')
126
+ code << "}\\n\" % [#{args.join(', ')}]\nend"
127
+
128
+ (class << layout; self end).class_eval(code, __FILE__, __LINE__)
129
+ end
130
+ # :startdoc:
131
+
132
+ # call-seq:
133
+ # Parseable.json( opts )
134
+ #
135
+ # Create a new Parseable layout that outputs log events usig JSON style
136
+ # formatting. See the initializer documentation for available options.
137
+ #
138
+ def self.json( opts = {} )
139
+ opts[:style] = 'json'
140
+ new(opts)
141
+ end
142
+
143
+ # call-seq:
144
+ # Parseable.yaml( opts )
145
+ #
146
+ # Create a new Parseable layout that outputs log events usig YAML style
147
+ # formatting. See the initializer documentation for available options.
148
+ #
149
+ def self.yaml( opts = {} )
150
+ opts[:style] = 'yaml'
151
+ new(opts)
152
+ end
153
+
154
+ # call-seq:
155
+ # Parseable.new( opts )
156
+ #
157
+ # Creates a new Parseable layout using the following options:
158
+ #
159
+ # :style => :json or :yaml
160
+ # :items => %w[timestamp level logger message]
161
+ #
162
+ def initialize( opts = {} )
163
+ super
164
+ @created_at = Time.now
165
+ @style = opts.getopt(:style, 'json').to_s.intern
166
+ self.items = opts.getopt(:items, %w[timestamp level logger message])
167
+ end
168
+
169
+ attr_reader :items
170
+
171
+ # call-seq:
172
+ # layout.items = %w[timestamp level logger message]
173
+ #
174
+ # Set the log event items that will be formatted by this layout. These
175
+ # items, and only these items, will appear in the log output.
176
+ #
177
+ def items=( ary )
178
+ @items = Array(ary).map {|name| name.to_s.downcase}
179
+ valid = DIRECTIVE_TABLE.keys
180
+ @items.each do |name|
181
+ raise ArgumentError, "unknown item - #{name.inspect}" unless valid.include? name
182
+ end
183
+ create_format_method
184
+ end
185
+
186
+
187
+ private
188
+
189
+ # Take the given _value_ and format it into a JSON compatible string.
190
+ #
191
+ def format_as_json( value )
192
+ case value
193
+ when String, Integer, Float; value.inspect
194
+ when nil; 'null'
195
+ else value.to_s.inspect end
196
+ end
197
+
198
+ # Call the appropriate class level create format method based on the
199
+ # style of this parseable layout.
200
+ #
201
+ def create_format_method
202
+ case @style
203
+ when :json; Parseable.create_json_format_method(self)
204
+ when :yaml; Parseable.create_yaml_format_method(self)
205
+ else raise ArgumentError, "unknown format style '#@style'" end
206
+ end
207
+
208
+ end # class Parseable
209
+ end # module Logging::Layouts
210
+
211
+ # EOF
@@ -1,6 +1,5 @@
1
1
 
2
- module Logging
3
- module Layouts
2
+ module Logging::Layouts
4
3
 
5
4
  # A flexible layout configurable with pattern string.
6
5
  #
@@ -22,7 +21,7 @@ module Layouts
22
21
  # Let the conversion pattern be "%-5l [%c]: %m\n" and assume that the
23
22
  # logging environment was set to use a Pattern layout. Then the statements
24
23
  #
25
- # root = Logging::Logger[:root]
24
+ # root = Logging.logger[:root]
26
25
  # root.debug("Message 1")
27
26
  # root.warn("Message 2")
28
27
  #
@@ -57,9 +56,9 @@ module Layouts
57
56
  # [t] Used to output the object ID of the thread that generated the
58
57
  # log event.
59
58
  # [T] Used to output the name of the thread that generated the log event.
60
- # Name can be specified using Thread.current[:name] notation. Output empty
61
- # string if name not specified. This options helps to create more human
62
- # readable output for multithread application log.
59
+ # Name can be specified using Thread.current[:name] notation. Output
60
+ # empty string if name not specified. This option helps to create
61
+ # more human readable output for multithread application logs.
63
62
  # [%] The sequence '%%' outputs a single percent sign.
64
63
  #
65
64
  # The directives F, L, and M will only work if the Logger generating the
@@ -290,7 +289,6 @@ module Layouts
290
289
  # :startdoc:
291
290
 
292
291
  end # class Pattern
293
- end # module Layouts
294
- end # module Logging
292
+ end # module Logging::Layouts
295
293
 
296
294
  # EOF
@@ -37,7 +37,7 @@ module Logging
37
37
 
38
38
  m = CALLER_RGXP.match(t)
39
39
  @file = m[1]
40
- @line = m[2]
40
+ @line = Integer(m[2])
41
41
  @method = m[3] unless m[3].nil?
42
42
  end
43
43
  end
@@ -12,8 +12,8 @@ module Logging
12
12
  #
13
13
  # Example:
14
14
  #
15
- # log = Logging::Logger['my logger']
16
- # log.add_appenders( Logging::Appender.stdout ) # append to STDOUT
15
+ # log = Logging.logger['my logger']
16
+ # log.add_appenders( Logging.appenders.stdout ) # append to STDOUT
17
17
  # log.level = :info # log 'info' and above
18
18
  #
19
19
  # log.info 'starting foo operation'
@@ -312,8 +312,8 @@ module Logging
312
312
  #
313
313
  def add_appenders( *args )
314
314
  args.flatten.each do |arg|
315
- o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appender[arg]
316
- raise ArgumentError, "unknown appender '#{arg}'" if o.nil?
315
+ o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appenders[arg.to_s]
316
+ raise ArgumentError, "unknown appender #{arg.inspect}" if o.nil?
317
317
  @appenders << o unless @appenders.include?(o)
318
318
  end
319
319
  self
@@ -11,7 +11,7 @@ module Spec
11
11
  to = opts.getopt(:to, '__rspec__')
12
12
  exclusive = opts.getopt(:exclusive, true)
13
13
 
14
- appender = Logging::Appender[to] || Logging::Appenders::StringIo.new(to)
14
+ appender = Logging::Appenders[to] || Logging::Appenders::StringIo.new(to)
15
15
  logger = Logging::Logger[from]
16
16
  if exclusive
17
17
  logger.appenders = appender
@@ -20,7 +20,7 @@ module Spec
20
20
  end
21
21
 
22
22
  before(:all) do
23
- @log_output = Logging::Appender[to]
23
+ @log_output = Logging::Appenders[to]
24
24
  end
25
25
 
26
26
  before(:each) do
@@ -9,20 +9,18 @@ module TestAppenders
9
9
 
10
10
  def setup
11
11
  super
12
- ::Logging.init
13
- @levels = ::Logging::LEVELS
14
-
15
- @appender = ::Logging::Appenders::StringIo.new(
12
+ @appender = Logging.appenders.string_io(
16
13
  'test_appender', :auto_flushing => 3, :immediate_at => :error
17
14
  )
15
+ @appender.clear
18
16
  @sio = @appender.sio
19
-
17
+ @levels = Logging::LEVELS
20
18
  begin readline rescue EOFError end
21
19
  end
22
20
 
23
21
  def test_append
24
- event = ::Logging::LogEvent.new('TestLogger', @levels['warn'],
25
- [1, 2, 3, 4], false)
22
+ event = Logging::LogEvent.new('TestLogger', @levels['warn'],
23
+ [1, 2, 3, 4], false)
26
24
  @appender.append event
27
25
  assert_nil(readline)
28
26
 
@@ -45,15 +43,15 @@ module TestAppenders
45
43
  def test_append_error
46
44
  # setup an internal logger to capture error messages from the IO
47
45
  # appender
48
- log = Logging::Appenders::StringIo.new('__internal_io')
49
- Logging::Logger[Logging].add_appenders(log)
50
- Logging::Logger[Logging].level = 'all'
46
+ log = Logging.appenders.string_io('__internal_io')
47
+ Logging.logger[Logging].add_appenders(log)
48
+ Logging.logger[Logging].level = 'all'
51
49
 
52
50
 
53
51
  # close the string IO object so we get an error
54
52
  @sio.close
55
- event = ::Logging::LogEvent.new('TestLogger', @levels['warn'],
56
- [1, 2, 3, 4], false)
53
+ event = Logging::LogEvent.new('TestLogger', @levels['warn'],
54
+ [1, 2, 3, 4], false)
57
55
  @appender.append event
58
56
  assert_nil(log.readline)
59
57
 
@@ -68,6 +66,16 @@ module TestAppenders
68
66
  assert_equal 5, @appender.level
69
67
  end
70
68
 
69
+ def test_auto_flushing
70
+ assert_raise(ArgumentError) {
71
+ @appender.auto_flushing = Object.new
72
+ }
73
+
74
+ assert_raise(ArgumentError) {
75
+ @appender.auto_flushing = -1
76
+ }
77
+ end
78
+
71
79
  def test_close
72
80
  assert_equal false, @sio.closed?
73
81
  assert_equal false, @appender.closed?
@@ -77,7 +85,7 @@ module TestAppenders
77
85
  assert_equal true, @appender.closed?
78
86
 
79
87
  [STDIN, STDERR, STDOUT].each do |io|
80
- @appender = ::Logging::Appenders::IO.new 'test', io
88
+ @appender = Logging.appenders.io('test', io)
81
89
  @appender.close
82
90
  assert_equal false, io.closed?
83
91
  assert_equal true, @appender.closed?
@@ -105,9 +113,9 @@ module TestAppenders
105
113
  def test_concat_error
106
114
  # setup an internal logger to capture error messages from the IO
107
115
  # appender
108
- log = Logging::Appenders::StringIo.new('__internal_io')
109
- Logging::Logger[Logging].add_appenders(log)
110
- Logging::Logger[Logging].level = 'all'
116
+ log = Logging.appenders.string_io('__internal_io')
117
+ Logging.logger[Logging].add_appenders(log)
118
+ Logging.logger[Logging].level = 'all'
111
119
 
112
120
  # close the string IO object so we get an error
113
121
  @sio.close
@@ -141,8 +149,8 @@ module TestAppenders
141
149
  end
142
150
 
143
151
  def test_immediate_at
144
- event = ::Logging::LogEvent.new('TestLogger', @levels['warn'],
145
- [1, 2, 3, 4], false)
152
+ event = Logging::LogEvent.new('TestLogger', @levels['warn'],
153
+ [1, 2, 3, 4], false)
146
154
  @appender.append event
147
155
  assert_nil(readline)
148
156