logging 1.4.3 → 1.5.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 (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