logging 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/History.txt +8 -0
  2. data/Rakefile +2 -4
  3. data/examples/colorization.rb +62 -0
  4. data/lib/logging.rb +22 -8
  5. data/lib/logging/appender.rb +7 -7
  6. data/lib/logging/appenders/buffering.rb +30 -24
  7. data/lib/logging/appenders/email.rb +7 -11
  8. data/lib/logging/appenders/file.rb +0 -1
  9. data/lib/logging/appenders/io.rb +8 -14
  10. data/lib/logging/appenders/rolling_file.rb +9 -13
  11. data/lib/logging/appenders/string_io.rb +0 -1
  12. data/lib/logging/appenders/syslog.rb +1 -1
  13. data/lib/logging/color_scheme.rb +249 -0
  14. data/lib/logging/layouts/basic.rb +2 -3
  15. data/lib/logging/layouts/parseable.rb +5 -7
  16. data/lib/logging/layouts/pattern.rb +77 -18
  17. data/lib/logging/log_event.rb +1 -1
  18. data/lib/logging/logger.rb +3 -4
  19. data/lib/logging/proxy.rb +57 -0
  20. data/lib/logging/utils.rb +3 -2
  21. data/test/appenders/test_buffered_io.rb +2 -7
  22. data/test/appenders/test_console.rb +1 -1
  23. data/test/appenders/test_email.rb +1 -1
  24. data/test/appenders/test_file.rb +1 -1
  25. data/test/appenders/test_growl.rb +1 -1
  26. data/test/appenders/test_io.rb +4 -5
  27. data/test/appenders/test_rolling_file.rb +1 -1
  28. data/test/appenders/test_syslog.rb +1 -1
  29. data/test/benchmark.rb +15 -11
  30. data/test/config/test_configurator.rb +1 -1
  31. data/test/config/test_yaml_configurator.rb +1 -1
  32. data/test/layouts/test_basic.rb +1 -1
  33. data/test/layouts/test_color_pattern.rb +90 -0
  34. data/test/layouts/test_json.rb +1 -1
  35. data/test/layouts/test_pattern.rb +4 -5
  36. data/test/layouts/test_yaml.rb +1 -1
  37. data/test/test_appender.rb +1 -1
  38. data/test/test_color_scheme.rb +39 -0
  39. data/test/test_consolidate.rb +1 -1
  40. data/test/test_layout.rb +1 -1
  41. data/test/test_log_event.rb +1 -1
  42. data/test/test_logger.rb +1 -1
  43. data/test/test_logging.rb +1 -1
  44. data/test/test_proxy.rb +67 -0
  45. data/test/test_repository.rb +2 -2
  46. data/test/test_root_logger.rb +1 -1
  47. data/test/test_stats.rb +1 -1
  48. data/test/test_utils.rb +1 -1
  49. data/version.txt +1 -1
  50. metadata +40 -17
@@ -65,4 +65,3 @@ module Logging::Appenders
65
65
  end # class StringIo
66
66
  end # module Logging::Appenders
67
67
 
68
- # EOF
@@ -131,7 +131,7 @@ module Logging::Appenders
131
131
  #
132
132
  def close( footer = true )
133
133
  super
134
- @syslog.close
134
+ @syslog.close if @syslog.opened?
135
135
  self
136
136
  end
137
137
 
@@ -0,0 +1,249 @@
1
+
2
+ # color_scheme.rb
3
+ #
4
+ # Created by Jeremy Hinegardner on 2007-01-24
5
+ # Copyright 2007. All rights reserved
6
+ #
7
+ # This is Free Software. See LICENSE and COPYING for details
8
+
9
+ module Logging
10
+
11
+ # ColorScheme objects encapsulate a named set of colors to be used in the
12
+ # colors() method call. For example, by applying a ColorScheme that
13
+ # has a <tt>:warning</tt> color then the following could be used:
14
+ #
15
+ # scheme.color("This is a warning", :warning)
16
+ #
17
+ # ColorScheme objects are used by the Pattern layout code to colorize log
18
+ # messages. Each color scheme is given a unique name which is used by the
19
+ # Pattern layout to lookup the appropriate color scheme to use. Please
20
+ # refere to the Pattern layout documentation for more details - specifically
21
+ # the initializer documentation.
22
+ #
23
+ # The color scheme can be applied to the Pattern layout in several ways.
24
+ # Each token in the log pattern can be colorized with the log level (debug,
25
+ # info, warn, etc) receiving unique colors based on the level itself.
26
+ # Another option is to colorize the enitre log message based on the log
27
+ # level; in this mode tokens do not get their own colors. Please see the
28
+ # ColorScheme initializer for the list of colorization options.
29
+ #
30
+ class ColorScheme
31
+
32
+ class << self
33
+ # Retrieve a color scheme by name.
34
+ #
35
+ def []( name )
36
+ @color_schemes[name.to_s]
37
+ end
38
+
39
+ # Store a color scheme by name.
40
+ #
41
+ def []=( name, value )
42
+ raise ArgumentError, "Silly! That's not a ColorSchmeme!" unless ColorScheme === value
43
+ @color_schemes[name.to_s] = value
44
+ end
45
+
46
+ # Clear all color schemes and setup a default color scheme.
47
+ #
48
+ def reset
49
+ @color_schemes ||= {}
50
+ @color_schemes.clear
51
+
52
+ new(:default, :levels => {
53
+ :info => :green,
54
+ :warn => :yellow,
55
+ :error => :red,
56
+ :fatal => [:white, :on_red]
57
+ })
58
+ end
59
+ end
60
+
61
+ # Create a ColorScheme instance that can be accessed using the given
62
+ # _name_. If a color scheme already exists with the given _name_ it will
63
+ # be replaced by the new color scheme.
64
+ #
65
+ # The color names are passed as options to the method with each name
66
+ # mapping to one or more color codes. For example:
67
+ #
68
+ # ColorScheme.new('example', :logger => [:white, :on_green], :message => :magenta)
69
+ #
70
+ # The color codes are the lowercase names of the constants defined at the
71
+ # end of this file. Multiple color codes can be aliased by grouping them
72
+ # in an array as shown in the example above.
73
+ #
74
+ # Since color schems are primary inteneded to be used with the Pattern
75
+ # layout, there are a few special options of note. First the log levels
76
+ # are enumerated in their own hash:
77
+ #
78
+ # :levels => {
79
+ # :debug => :blue,
80
+ # :info => :cyan,
81
+ # :warn => :yellow,
82
+ # :error => :red,
83
+ # :fatal => [:white, :on_red]
84
+ # }
85
+ #
86
+ # The log level token will be colorized differently based on the value of
87
+ # the log level itself. Similarly the entire log message can be colorized
88
+ # based on the value of the log level. A dfferent option should be given
89
+ # for this behavior:
90
+ #
91
+ # :lines => {
92
+ # :debug => :blue,
93
+ # :info => :cyan,
94
+ # :warn => :yellow,
95
+ # :error => :red,
96
+ # :fatal => [:white, :on_red]
97
+ # }
98
+ #
99
+ # The :levels and :lines options cannot be used together; only one or the
100
+ # other should be given.
101
+ #
102
+ # The remaining tokens defined in the Pattern layout can be colorized
103
+ # using the following aliases. Their meaning in the Pattern layout are
104
+ # repeated here for sake of clarity.
105
+ #
106
+ # :logger [%c] name of the logger that generate the log event
107
+ # :date [%d] datestamp
108
+ # :message [%m] the user supplied log message
109
+ # :pid [%p] PID of the current process
110
+ # :time [%r] the time in milliseconds since the program started
111
+ # :thread [%T] the name of the thread Thread.current[:name]
112
+ # :thread_id [%t] object_id of the thread
113
+ # :file [%F] filename where the logging request was issued
114
+ # :line [%L] line number where the logging request was issued
115
+ # :method [%M] method name where the logging request was issued
116
+ #
117
+ # Please refer to the "examples/colorization.rb" file for a working
118
+ # example of log colorization.
119
+ #
120
+ def initialize( name, opts = {} )
121
+ @scheme = Hash.new
122
+
123
+ @lines = opts.key? :lines
124
+ @levels = opts.key? :levels
125
+ raise ArgumentError, "Found both :lines and :levels - only one can be used." if lines? and levels?
126
+
127
+ lines = opts.delete :lines
128
+ levels = opts.delete :levels
129
+
130
+ load_from_hash(opts)
131
+ load_from_hash(lines) if lines?
132
+ load_from_hash(levels) if levels?
133
+
134
+ ::Logging::ColorScheme[name] = self
135
+ end
136
+
137
+ # Load multiple colors from key/value pairs.
138
+ #
139
+ def load_from_hash( h )
140
+ h.each_pair do |color_tag, constants|
141
+ self[color_tag] = constants
142
+ end
143
+ end
144
+
145
+ # Returns +true+ if the :lines option was passed to the constructor.
146
+ #
147
+ def lines?
148
+ @lines
149
+ end
150
+
151
+ # Returns +true+ if the :levels option was passed to the constructor.
152
+ #
153
+ def levels?
154
+ @levels
155
+ end
156
+
157
+ # Does this color scheme include the given tag name?
158
+ #
159
+ def include?( color_tag )
160
+ @scheme.key?(to_key(color_tag))
161
+ end
162
+
163
+ # Allow the scheme to be accessed like a Hash.
164
+ #
165
+ def []( color_tag )
166
+ @scheme[to_key(color_tag)]
167
+ end
168
+
169
+ # Allow the scheme to be set like a Hash.
170
+ #
171
+ def []=( color_tag, constants )
172
+ @scheme[to_key(color_tag)] = constants.respond_to?(:map) ?
173
+ constants.map { |c| to_constant(c) }.join : to_constant(constants)
174
+ end
175
+
176
+ # This method provides easy access to ANSI color sequences, without the user
177
+ # needing to remember to CLEAR at the end of each sequence. Just pass the
178
+ # _string_ to color, followed by a list of _colors_ you would like it to be
179
+ # affected by. The _colors_ can be ColorScheme class constants, or symbols
180
+ # (:blue for BLUE, for example). A CLEAR will automatically be embedded to
181
+ # the end of the returned String.
182
+ #
183
+ def color( string, *colors )
184
+ colors.map! { |color|
185
+ color_tag = to_key(color)
186
+ @scheme.key?(color_tag) ? @scheme[color_tag] : to_constant(color)
187
+ }
188
+
189
+ colors.compact!
190
+ return string if colors.empty?
191
+
192
+ "#{colors.join}#{string}#{CLEAR}"
193
+ end
194
+
195
+ private
196
+
197
+ # Return a normalized representation of a color name.
198
+ #
199
+ def to_key( t )
200
+ t.to_s.downcase
201
+ end
202
+
203
+ # Return a normalized representation of a color setting.
204
+ #
205
+ def to_constant( v )
206
+ ColorScheme.const_get(v.to_s.upcase)
207
+ rescue NameError
208
+ return nil
209
+ end
210
+
211
+ # Embed in a String to clear all previous ANSI sequences. This *MUST* be
212
+ # done before the program exits!
213
+ CLEAR = "\e[0m".freeze
214
+ RESET = CLEAR # An alias for CLEAR.
215
+ ERASE_LINE = "\e[K".freeze # Erase the current line of terminal output.
216
+ ERASE_CHAR = "\e[P".freeze # Erase the character under the cursor.
217
+ BOLD = "\e[1m".freeze # The start of an ANSI bold sequence.
218
+ DARK = "\e[2m".freeze # The start of an ANSI dark sequence. (Terminal support uncommon.)
219
+ UNDERLINE = "\e[4m".freeze # The start of an ANSI underline sequence.
220
+ UNDERSCORE = UNDERLINE # An alias for UNDERLINE.
221
+ BLINK = "\e[5m".freeze # The start of an ANSI blink sequence. (Terminal support uncommon.)
222
+ REVERSE = "\e[7m".freeze # The start of an ANSI reverse sequence.
223
+ CONCEALED = "\e[8m".freeze # The start of an ANSI concealed sequence. (Terminal support uncommon.)
224
+
225
+ BLACK = "\e[30m".freeze # Set the terminal's foreground ANSI color to black.
226
+ RED = "\e[31m".freeze # Set the terminal's foreground ANSI color to red.
227
+ GREEN = "\e[32m".freeze # Set the terminal's foreground ANSI color to green.
228
+ YELLOW = "\e[33m".freeze # Set the terminal's foreground ANSI color to yellow.
229
+ BLUE = "\e[34m".freeze # Set the terminal's foreground ANSI color to blue.
230
+ MAGENTA = "\e[35m".freeze # Set the terminal's foreground ANSI color to magenta.
231
+ CYAN = "\e[36m".freeze # Set the terminal's foreground ANSI color to cyan.
232
+ WHITE = "\e[37m".freeze # Set the terminal's foreground ANSI color to white.
233
+
234
+ ON_BLACK = "\e[40m".freeze # Set the terminal's background ANSI color to black.
235
+ ON_RED = "\e[41m".freeze # Set the terminal's background ANSI color to red.
236
+ ON_GREEN = "\e[42m".freeze # Set the terminal's background ANSI color to green.
237
+ ON_YELLOW = "\e[43m".freeze # Set the terminal's background ANSI color to yellow.
238
+ ON_BLUE = "\e[44m".freeze # Set the terminal's background ANSI color to blue.
239
+ ON_MAGENTA = "\e[45m".freeze # Set the terminal's background ANSI color to magenta.
240
+ ON_CYAN = "\e[46m".freeze # Set the terminal's background ANSI color to cyan.
241
+ ON_WHITE = "\e[47m".freeze # Set the terminal's background ANSI color to white.
242
+
243
+ end # ColorScheme
244
+
245
+ # setup the default color scheme
246
+ ColorScheme.reset
247
+
248
+ end # Logging
249
+
@@ -26,7 +26,6 @@ module Logging::Layouts
26
26
  ::Logging::LNAMES[event.level], event.logger, obj)
27
27
  end
28
28
 
29
- end # class Basic
30
- end # module Logging::Layouts
29
+ end # Basic
30
+ end # Logging::Layouts
31
31
 
32
- # EOF
@@ -65,7 +65,7 @@ module Logging::Layouts
65
65
  # own line in the log output. Therefore, to parse the output you must read
66
66
  # it line by line and parse the individual objects. Taking the same
67
67
  # example above the JSON output would be:
68
- #
68
+ #
69
69
  # {"timestamp":"2009-04-17 16:15:42","level":"INFO","logger":"Foo::Bar","message":"this is a log message"}
70
70
  # {"timestamp":"2009-04-17 16:15:43","level":"ERROR","logger":"Foo","message":"<RuntimeError> Oooops!!"}
71
71
  #
@@ -124,7 +124,7 @@ module Logging::Layouts
124
124
  "\\\"#{name}\\\":%s"
125
125
  }.join(',')
126
126
  code << "}\\n\" % [#{args.join(', ')}]\nend"
127
-
127
+
128
128
  (class << layout; self end).class_eval(code, __FILE__, __LINE__)
129
129
  end
130
130
  # :startdoc:
@@ -183,8 +183,7 @@ module Logging::Layouts
183
183
  create_format_method
184
184
  end
185
185
 
186
-
187
- private
186
+ private
188
187
 
189
188
  # Take the given _value_ and format it into a JSON compatible string.
190
189
  #
@@ -205,7 +204,6 @@ module Logging::Layouts
205
204
  else raise ArgumentError, "unknown format style '#@style'" end
206
205
  end
207
206
 
208
- end # class Parseable
209
- end # module Logging::Layouts
207
+ end # Parseable
208
+ end # Logging::Layouts
210
209
 
211
- # EOF
@@ -35,7 +35,7 @@ module Logging::Layouts
35
35
  # conversion specifier when it reads a conversion character. In the example
36
36
  # above the conversion specifier %-5l means the level of the logging event
37
37
  # should be left justified to a width of five characters. The recognized
38
- # conversion characters are
38
+ # conversion characters are
39
39
  #
40
40
  # [c] Used to output the name of the logger that generated the log
41
41
  # event. Supports an optional "precision" described further below.
@@ -95,7 +95,7 @@ module Logging::Layouts
95
95
  #
96
96
  # Below are various format modifier examples for the category conversion
97
97
  # specifier.
98
- #
98
+ #
99
99
  # [%20c] Left pad with spaces if the logger name is less than 20
100
100
  # characters long
101
101
  # [%-20c] Right pad with spaces if the logger name is less than 20
@@ -149,6 +149,20 @@ module Logging::Layouts
149
149
  # default date format
150
150
  ISO8601 = "%Y-%m-%d %H:%M:%S".freeze
151
151
 
152
+ # Human name aliases for directives - used for colorization of tokens
153
+ COLOR_ALIAS_TABLE = {
154
+ 'c' => :logger,
155
+ 'd' => :date,
156
+ 'm' => :message,
157
+ 'p' => :pid,
158
+ 'r' => :time,
159
+ 'T' => :thread,
160
+ 't' => :thread_id,
161
+ 'F' => :file,
162
+ 'L' => :line,
163
+ 'M' => :method
164
+ }.freeze
165
+
152
166
  # call-seq:
153
167
  # Pattern.create_date_format_methods( pf )
154
168
  #
@@ -186,19 +200,23 @@ module Logging::Layouts
186
200
  #
187
201
  def self.create_format_method( pf )
188
202
  # Create the format(event) method
189
- code = "undef :format if method_defined? :format\n"
190
- code << "def format( event )\nsprintf(\""
203
+ format_string = '"'
191
204
  pattern = pf.pattern.dup
205
+ color_scheme = pf.color_scheme
192
206
  args = []
207
+ name_map_count = 0
193
208
 
194
209
  while true
195
210
  m = DIRECTIVE_RGXP.match(pattern)
196
- code << m[1] unless m[1].empty?
211
+ format_string << m[1] unless m[1].empty?
197
212
 
198
213
  case m[3]
199
- when '%'; code << '%%'
200
- when 'c'
201
- code << m[2] + 's'
214
+ when '%'; format_string << '%%'
215
+ when 'c'
216
+ fmt = m[2] + 's'
217
+ fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[m[3]]) if color_scheme and !color_scheme.lines?
218
+
219
+ format_string << fmt
202
220
  args << DIRECTIVE_TABLE[m[3]].dup
203
221
  if m[4]
204
222
  raise ArgumentError, "logger name precision must be an integer greater than zero: #{m[4]}" unless Integer(m[4]) > 0
@@ -206,9 +224,28 @@ module Logging::Layouts
206
224
  ".split(::Logging::Repository::PATH_DELIMITER)" \
207
225
  ".last(#{m[4]}).join(::Logging::Repository::PATH_DELIMITER)"
208
226
  end
227
+ when 'l'
228
+ if color_scheme and color_scheme.levels?
229
+ name_map = ::Logging::LNAMES.map { |name| color_scheme.color(("#{m[2]}s" % name), name) }
230
+ var = "@name_map_#{name_map_count}"
231
+ pf.instance_variable_set(var.to_sym, name_map)
232
+ name_map_count += 1
233
+
234
+ format_string << '%s'
235
+ format_string << "{#{m[4]}}" if m[4]
236
+ args << "#{var}[event.level]"
237
+ else
238
+ format_string << m[2] + 's'
239
+ format_string << "{#{m[4]}}" if m[4]
240
+ args << DIRECTIVE_TABLE[m[3]]
241
+ end
242
+
209
243
  when *DIRECTIVE_TABLE.keys
210
- code << m[2] + 's'
211
- code << "{#{m[4]}}" if m[4]
244
+ fmt = m[2] + 's'
245
+ fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[m[3]]) if color_scheme and !color_scheme.lines?
246
+
247
+ format_string << fmt
248
+ format_string << "{#{m[4]}}" if m[4]
212
249
  args << DIRECTIVE_TABLE[m[3]]
213
250
  when nil; break
214
251
  else
@@ -219,10 +256,19 @@ module Logging::Layouts
219
256
  pattern = m[5]
220
257
  end
221
258
 
222
- code << '"'
223
- code << ', ' + args.join(', ') unless args.empty?
224
- code << ")\n"
225
- code << "end\n"
259
+ format_string << '"'
260
+
261
+ sprintf = "sprintf("
262
+ sprintf << format_string
263
+ sprintf << ', ' + args.join(', ') unless args.empty?
264
+ sprintf << ")"
265
+
266
+ if color_scheme and color_scheme.lines?
267
+ sprintf = "color_scheme.color(#{sprintf}, ::Logging::LNAMES[event.level])"
268
+ end
269
+
270
+ code = "undef :format if method_defined? :format\n"
271
+ code << "def format( event )\n#{sprintf}\nend\n"
226
272
  ::Logging.log_internal(0) {code}
227
273
 
228
274
  pf._meta_eval(code, __FILE__, __LINE__)
@@ -237,9 +283,16 @@ module Logging::Layouts
237
283
  # :pattern => "[%d] %-5l -- %c : %m\n"
238
284
  # :date_pattern => "%Y-%m-%d %H:%M:%S"
239
285
  # :date_method => 'usec' or 'to_s'
286
+ # :color_scheme => :default
240
287
  #
241
288
  # If used, :date_method will supersede :date_pattern.
242
289
  #
290
+ # The :color_scheme is used to apply color formatting to the log messages.
291
+ # Individual tokens can be colorized witch the level token [%l] receiving
292
+ # distinct colors based on the level of the log event. The entire
293
+ # generated log message can also be colorized based on the level of the
294
+ # log event. See the ColorScheme documentation for more details.
295
+ #
243
296
  def initialize( opts = {} )
244
297
  super
245
298
  @created_at = Time.now
@@ -251,11 +304,18 @@ module Logging::Layouts
251
304
  @pattern = opts.getopt(:pattern,
252
305
  "[%d] %-#{::Logging::MAX_LEVEL_LENGTH}l -- %c : %m\n")
253
306
 
307
+ cs_name = opts.getopt(:color_scheme)
308
+ @color_scheme =
309
+ case cs_name
310
+ when false, nil; nil
311
+ when true; ::Logging::ColorScheme[:default]
312
+ else ::Logging::ColorScheme[cs_name] end
313
+
254
314
  Pattern.create_date_format_methods(self)
255
315
  Pattern.create_format_method(self)
256
316
  end
257
317
 
258
- attr_reader :pattern, :date_pattern, :date_method
318
+ attr_reader :pattern, :date_pattern, :date_method, :color_scheme
259
319
 
260
320
  # call-seq:
261
321
  # appender.pattern = "[%d] %-5l -- %c : %m\n"
@@ -305,7 +365,6 @@ module Logging::Layouts
305
365
  end
306
366
  # :startdoc:
307
367
 
308
- end # class Pattern
309
- end # module Logging::Layouts
368
+ end # Pattern
369
+ end # Logging::Layouts
310
370
 
311
- # EOF