TwP-logging 0.9.8 → 0.9.8.1

Sign up to get free protection for your applications and to get access to all the features.
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