TwP-logging 0.9.8 → 0.9.8.1

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.
data/History.txt CHANGED
@@ -1,7 +1,22 @@
1
+ == 1.0.0 / 2009-04-17
2
+
3
+ 2 major enhancements
4
+ - Refactored access to the appenders
5
+ - Created a much cleaner way to initialize the logging framework
6
+ 3 minor enhancements
7
+ - Added a YAML layout option
8
+ - Added a JSON layout option
9
+ - Cration of an "examples" directory or logging configurations
10
+ 1 bug fix
11
+ - Logging initialization happens implicitly when a logger, layout, or
12
+ appender is created
13
+
1
14
  == 0.9.8 / 2009-04-11
2
15
 
3
- 1 minor enhancement
16
+ 2 minor enhancements
4
17
  - Adding a to_s method to the StringIo appender's StringIO object
18
+ - Added a Spec::LoggingHelper class that will capture log messages
19
+ when using rspec style testing
5
20
 
6
21
  == 0.9.7 / 2009-03-17
7
22
 
data/Rakefile CHANGED
@@ -25,7 +25,7 @@ PROJ.version = Logging::VERSION
25
25
  PROJ.readme_file = 'README.rdoc'
26
26
  PROJ.ignore_file = '.gitignore'
27
27
 
28
- PROJ.exclude << %w[^tags$]
28
+ PROJ.exclude << %w[^tags$ logging.gemspec]
29
29
  PROJ.rdoc.exclude << '^data'
30
30
  #PROJ.rdoc.dir = 'doc/rdoc'
31
31
  #PROJ.rdoc.remote_dir = 'rdoc'
@@ -16,58 +16,6 @@ module Logging
16
16
  #
17
17
  class Appender
18
18
 
19
- @appenders = Hash.new
20
-
21
- class << self
22
-
23
- # call-seq:
24
- # Appender[name]
25
- #
26
- # Returns the appender instance stroed in the Appender hash under the
27
- # key _name_, or +nil+ if no appender has been created using that name.
28
- #
29
- def []( name ) @appenders[name] end
30
-
31
- # call-seq:
32
- # Appender[name] = appender
33
- #
34
- # Stores the given _appender_ instance in the Appender hash under the
35
- # key _name_.
36
- #
37
- def []=( name, val ) @appenders[name] = val end
38
-
39
- # call-seq:
40
- # Appenders.remove( name )
41
- #
42
- # Removes the appender instance stored in the Appender hash under the
43
- # key _name_.
44
- #
45
- def remove( name ) @appenders.delete(name) end
46
-
47
- # call-seq:
48
- # Appender.stdout
49
- #
50
- # Returns an instance of the Stdout Appender. Unless the user explicitly
51
- # creates a new Stdout Appender, the instance returned by this method
52
- # will always be the same:
53
- #
54
- # Appender.stdout.object_id == Appender.stdout.object_id #=> true
55
- #
56
- def stdout( ) self['stdout'] || ::Logging::Appenders::Stdout.new end
57
-
58
- # call-seq:
59
- # Appender.stderr
60
- #
61
- # Returns an instance of the Stderr Appender. Unless the user explicitly
62
- # creates a new Stderr Appender, the instance returned by this method
63
- # will always be the same:
64
- #
65
- # Appender.stderr.object_id == Appender.stderr.object_id #=> true
66
- #
67
- def stderr( ) self['stderr'] || ::Logging::Appenders::Stderr.new end
68
-
69
- end # class << self
70
-
71
19
  attr_reader :name, :layout, :level
72
20
 
73
21
  # call-seq:
@@ -85,6 +33,8 @@ class Appender
85
33
  # :level => the level at which to log
86
34
  #
87
35
  def initialize( name, opts = {} )
36
+ ::Logging.init unless ::Logging.const_defined? :MAX_LEVEL_LENGTH
37
+
88
38
  @name = name.to_s
89
39
  @closed = false
90
40
 
@@ -102,7 +52,7 @@ class Appender
102
52
  end
103
53
  end
104
54
 
105
- ::Logging::Appender[@name] = self
55
+ ::Logging::Appenders[@name] = self
106
56
  end
107
57
 
108
58
  # call-seq:
@@ -217,7 +167,7 @@ class Appender
217
167
  #
218
168
  def close( footer = true )
219
169
  return self if @closed
220
- ::Logging::Appender.remove(@name)
170
+ ::Logging::Appenders.remove(@name)
221
171
  @closed = true
222
172
 
223
173
  sync {flush}
@@ -298,6 +248,4 @@ class Appender
298
248
  end # class Appender
299
249
  end # module Logging
300
250
 
301
- Logging.require_all_libs_relative_to(__FILE__, 'appenders')
302
-
303
251
  # EOF
@@ -117,8 +117,9 @@ module Logging::Appenders
117
117
  # options.
118
118
  #
119
119
  def configure_buffering( opts )
120
- @buffer = []
120
+ ::Logging.init unless ::Logging.const_defined? :MAX_LEVEL_LENGTH
121
121
 
122
+ @buffer = []
122
123
  self.immediate_at = opts.getopt(:immediate_at, '')
123
124
  self.auto_flushing = opts.getopt(:auto_flushing, true)
124
125
  end
@@ -1,6 +1,4 @@
1
1
 
2
- require Logging.libpath(*%w[logging appenders io])
3
-
4
2
  module Logging::Appenders
5
3
 
6
4
  # This class provides an Appender that can write to STDOUT.
@@ -1,6 +1,4 @@
1
1
 
2
- require 'lockfile'
3
-
4
2
  module Logging::Appenders
5
3
 
6
4
  # An appender that writes to a file and ensures that the file size or age
@@ -1,6 +1,4 @@
1
1
 
2
- require 'stringio'
3
-
4
2
  module Logging::Appenders
5
3
 
6
4
  # This class provides an Appender that can write to a StringIO instance.
@@ -20,24 +18,8 @@ module Logging::Appenders
20
18
  def initialize( name, opts = {} )
21
19
  @sio = StringIO.new
22
20
  @sio.extend IoToS
21
+ @pos = 0
23
22
  super(name, @sio, opts)
24
- clear
25
- end
26
-
27
- # Read a single line of text from the internal StringIO instance. +nil+
28
- # is returned if the StringIO buffer is empty.
29
- #
30
- def readline
31
- sync {
32
- begin
33
- @sio.seek @pos
34
- line = @sio.readline
35
- @pos = @sio.tell
36
- line
37
- rescue EOFError
38
- nil
39
- end
40
- }
41
23
  end
42
24
 
43
25
  # Clears the internal StringIO instance. All log messages are removed
@@ -52,6 +34,23 @@ module Logging::Appenders
52
34
  end
53
35
  alias :reset :clear
54
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
+
55
54
  # :stopdoc:
56
55
  module IoToS
57
56
  def to_s
@@ -1,11 +1,4 @@
1
1
 
2
- begin
3
- require 'syslog'
4
- HAVE_SYSLOG = true
5
- rescue LoadError
6
- HAVE_SYSLOG = false
7
- end
8
-
9
2
  # only load this class if we have the syslog library
10
3
  # Windows does not have syslog
11
4
  #
@@ -0,0 +1,120 @@
1
+
2
+ module Logging
3
+ module Appenders
4
+
5
+ # Accessor / Factory for the Email appender.
6
+ #
7
+ def email( *args )
8
+ return ::Logging::Appenders::Email if args.empty?
9
+ ::Logging::Appenders::Email.new(*args)
10
+ end
11
+
12
+ # Accessor / Factory for the File appender.
13
+ #
14
+ def file( *args )
15
+ return ::Logging::Appenders::File if args.empty?
16
+ ::Logging::Appenders::File.new(*args)
17
+ end
18
+
19
+ # Accessor / Factory for the Growl appender.
20
+ #
21
+ def growl( *args )
22
+ return ::Logging::Appenders::Growl if args.empty?
23
+ ::Logging::Appenders::Growl.new(*args)
24
+ end
25
+
26
+ # Accessor / Factory for the IO appender.
27
+ #
28
+ def io( *args )
29
+ return ::Logging::Appenders::IO if args.empty?
30
+ ::Logging::Appenders::IO.new(*args)
31
+ end
32
+
33
+ # Accessor / Factory for the RollingFile appender.
34
+ #
35
+ def rolling_file( *args )
36
+ return ::Logging::Appenders::RollingFile if args.empty?
37
+ ::Logging::Appenders::RollingFile.new(*args)
38
+ end
39
+
40
+ # Accessor / Factory for the Stderr appender.
41
+ #
42
+ def stderr( *args )
43
+ if args.empty?
44
+ return self['stderr'] || ::Logging::Appenders::Stderr.new
45
+ end
46
+ ::Logging::Appenders::Stderr.new(*args)
47
+ end
48
+
49
+ # Accessor / Factory for the Stdout appender.
50
+ #
51
+ def stdout( *args )
52
+ if args.empty?
53
+ return self['stdout'] || ::Logging::Appenders::Stdout.new
54
+ end
55
+ ::Logging::Appenders::Stdout.new(*args)
56
+ end
57
+
58
+ # Accessor / Factory for the StringIo appender.
59
+ #
60
+ def string_io( *args )
61
+ return ::Logging::Appenders::StringIo if args.empty?
62
+ ::Logging::Appenders::StringIo.new(*args)
63
+ end
64
+
65
+ if HAVE_SYSLOG
66
+ # Accessor / Factory for the Syslog appender.
67
+ #
68
+ def syslog( *args )
69
+ return ::Logging::Appenders::Syslog if args.empty?
70
+ ::Logging::Appenders::Syslog.new(*args)
71
+ end
72
+ end
73
+
74
+ # call-seq:
75
+ # Appenders[name]
76
+ #
77
+ # Returns the appender instance stroed in the appender hash under the
78
+ # key _name_, or +nil+ if no appender has been created using that name.
79
+ #
80
+ def []( name ) @appenders[name] end
81
+
82
+ # call-seq:
83
+ # Appenders[name] = appender
84
+ #
85
+ # Stores the given _appender_ instance in the appender hash under the
86
+ # key _name_.
87
+ #
88
+ def []=( name, value ) @appenders[name] = value end
89
+
90
+ # call-seq:
91
+ # Appenders.remove( name )
92
+ #
93
+ # Removes the appender instance stored in the appender hash under the
94
+ # key _name_.
95
+ #
96
+ def remove( name ) @appenders.delete(name) end
97
+
98
+ # call-seq:
99
+ # each {|appender| block}
100
+ #
101
+ # Yield each appender to the _block_.
102
+ #
103
+ def each( &block )
104
+ @appenders.values.each(&block)
105
+ nil
106
+ end
107
+
108
+ extend self
109
+ @appenders = Hash.new
110
+
111
+ end # module Appenders
112
+ end # module Logging
113
+
114
+
115
+ %w[buffering io console email file growl rolling_file string_io syslog].
116
+ each do |fn|
117
+ require ::Logging.libpath('logging', 'appenders', fn)
118
+ end
119
+
120
+ # EOF
@@ -82,7 +82,7 @@ module Logging::Config
82
82
  l.additive = config[:additive] if l.respond_to? :additive=
83
83
  l.trace = config[:trace]
84
84
  l.appenders = Array(config[:appenders]).
85
- map {|name| ::Logging::Appender[name]}
85
+ map {|nm| ::Logging::Appenders[nm]}
86
86
  end
87
87
  end
88
88
 
@@ -1,8 +1,5 @@
1
1
 
2
- require 'yaml'
3
-
4
- module Logging
5
- module Config
2
+ module Logging::Config
6
3
 
7
4
  # The YamlConfigurator class is used to configure the Logging framework
8
5
  # using information found in a YAML file.
@@ -137,7 +134,7 @@ module Config
137
134
  l.trace = config['trace'] if l.respond_to? :trace=
138
135
 
139
136
  if config.has_key?('appenders')
140
- l.appenders = config['appenders'].map {|n| ::Logging::Appender[n]}
137
+ l.appenders = config['appenders'].map {|n| ::Logging::Appenders[n]}
141
138
  end
142
139
  end
143
140
  end
@@ -189,7 +186,6 @@ module Config
189
186
  end
190
187
 
191
188
  end # class YamlConfigurator
192
- end # module Config
193
- end # module Logging
189
+ end # module Logging::Config
194
190
 
195
191
  # EOF
@@ -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
@@ -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