buffered_logger 0.0.2 → 0.1.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.
- data/README +105 -0
- data/TODO +3 -0
- data/lib/buffered_logger/buffering.rb +66 -0
- data/lib/buffered_logger/formatting.rb +92 -0
- data/lib/buffered_logger/indentation.rb +61 -0
- data/lib/buffered_logger/logger.rb +114 -0
- data/lib/buffered_logger/thread_hash.rb +12 -0
- data/lib/buffered_logger.rb +6 -57
- data/spec/buffering_spec.rb +12 -0
- data/spec/formatting_spec.rb +120 -0
- data/spec/indentation_spec.rb +92 -0
- data/spec/logger_spec.rb +75 -0
- data/spec/spec_helper.rb +4 -0
- metadata +17 -5
data/README
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
ABOUT
|
2
|
+
-------------------------------------------------------------------------------
|
3
|
+
The 'buffered_logger' gem is an extension to ActiveSupport::BufferedLogger which
|
4
|
+
gives the the little logger some much needed formatting support. The actual
|
5
|
+
logging work is still delegated to to active_support/buffered_logger.
|
6
|
+
|
7
|
+
|
8
|
+
FEATURES
|
9
|
+
-------------------------------------------------------------------------------
|
10
|
+
- per thread indentation
|
11
|
+
- per thread auto flush setting
|
12
|
+
- temporary indentation blocks
|
13
|
+
- per severity formatting
|
14
|
+
- ansi color output formats
|
15
|
+
|
16
|
+
|
17
|
+
USAGE
|
18
|
+
-------------------------------------------------------------------------------
|
19
|
+
# default initialization
|
20
|
+
l = BufferedLogger.new(STDOUT)
|
21
|
+
|
22
|
+
# changing the severity level
|
23
|
+
l.level = :warn
|
24
|
+
l.level = 1
|
25
|
+
|
26
|
+
# formatter constructor (initializes the master thread's formatters)
|
27
|
+
l = BufferedLogger.new(STDOUT, :debug,
|
28
|
+
{ :info => "$green INFO: $white %s",
|
29
|
+
:warn => "$yellow WARNING: $white %s",
|
30
|
+
:error => "$red ERROR: $white %s" } )
|
31
|
+
|
32
|
+
# setting formatting after initialization
|
33
|
+
l.info_formatter = "$green INFO: $white %s",
|
34
|
+
l.warn_formatter = "$yellow WARNING: $white %s"
|
35
|
+
l.error_formatter = "$red ERROR: $white %s"
|
36
|
+
|
37
|
+
# checking the current thread's formatting
|
38
|
+
l.info_formatter.to_s
|
39
|
+
l.warn_formatter.to_s
|
40
|
+
...
|
41
|
+
|
42
|
+
# checking the current thread's indentation
|
43
|
+
l.padding.to_s
|
44
|
+
|
45
|
+
# setting the current thread's indentation
|
46
|
+
l.indent(4) # move cursor right 4 spaces
|
47
|
+
l.indent(-2) # move cursor left 2 spaces
|
48
|
+
l.indent(:reset) # reset indentation
|
49
|
+
|
50
|
+
# temporarily indenting using an indent block
|
51
|
+
l.indent do
|
52
|
+
l.info 'some info'
|
53
|
+
...
|
54
|
+
l.info 'some more info'
|
55
|
+
end
|
56
|
+
|
57
|
+
# setting auto_flushing
|
58
|
+
l.auto_flushing = 2
|
59
|
+
|
60
|
+
|
61
|
+
USAGE DETAILS
|
62
|
+
-------------------------------------------------------------------------------
|
63
|
+
* Severity Levels
|
64
|
+
- severity level is optional and defaults to :debug
|
65
|
+
- severity level can be provided as an Integer or Symbol
|
66
|
+
0 - DEBUG
|
67
|
+
1 - INFO
|
68
|
+
2 - WARN
|
69
|
+
3 - ERROR
|
70
|
+
4 - FATAL
|
71
|
+
5 - UNKNOWN
|
72
|
+
|
73
|
+
* ActiveSupport::BufferedLogger
|
74
|
+
- native buffered_logger functionality is preserved
|
75
|
+
$ ActiveSupport::BufferedLogger.public_instance_methods(false)
|
76
|
+
=> ["add", "info", "fatal?", "auto_flushing", "fatal", "silencer", "debug",
|
77
|
+
"info?", "warn?", "auto_flushing=", "silencer=", "level", "error?", "unknown",
|
78
|
+
"debug?", "flush", "level=", "open_log", "warn", "unknown?", "silence",
|
79
|
+
"error", "close"]
|
80
|
+
|
81
|
+
* ANSI Color
|
82
|
+
- colors are only supported when logging to STDOUT
|
83
|
+
- any ansi method may be used in a format string following a '$' symbol
|
84
|
+
$ Term::ANSIColor.instance_methods
|
85
|
+
=> ["black", "on_yellow", "underscore", "rapid_blink", "white", "bold",
|
86
|
+
"yellow", "on_cyan", "strikethrough", "on_green", "uncolored", "blink",
|
87
|
+
"cyan", "reset", "green", "on_magenta", "clear", "concealed", "on_red",
|
88
|
+
"dark", "underline", "magenta", "red", "on_blue", "negative", "on_black",
|
89
|
+
"italic", "blue", "on_white"]
|
90
|
+
|
91
|
+
* Indentation
|
92
|
+
- the padding is calculated on a per thread basis and each thread has an
|
93
|
+
indentation counter which is shared across all severity levels
|
94
|
+
|
95
|
+
* Formatting
|
96
|
+
- formatters are set on a per thread/per severity basis, meaning that every
|
97
|
+
severity level for every thread allows for a custom formatter
|
98
|
+
- the formatter search order is as follows
|
99
|
+
1. current thread's formatter for specified severity
|
100
|
+
2. master thread's formatter for specified severity
|
101
|
+
3. master thread's default formatter
|
102
|
+
|
103
|
+
* Thread Buffering
|
104
|
+
- the auto_flushing setting is set per thread and each thread defaults to
|
105
|
+
the autoflush setting of the master thread unless explicitly configured
|
data/TODO
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module BufferedLogger::Buffering
|
2
|
+
MAX_BUFFER_SIZE = 1000
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@buffer = Hash.new { |h,k| h[k] = [] }
|
6
|
+
@auto_flushing = BufferedLogger::ThreadHash.new
|
7
|
+
@auto_flushing[master_thread] = 1
|
8
|
+
@guard = Mutex.new
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
|
12
|
+
def buffer_text
|
13
|
+
buffer.join('')
|
14
|
+
end
|
15
|
+
|
16
|
+
def auto_flushing
|
17
|
+
@auto_flushing[Thread.current] || @auto_flushing[master_thread]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set the auto-flush period. Set to true to flush after every log message,
|
21
|
+
# to an integer to flush every N messages, or to false, nil, or zero to
|
22
|
+
# never auto-flush. If you turn auto-flushing off, be sure to regularly
|
23
|
+
# flush the log yourself -- it will eat up memory until you do.
|
24
|
+
def auto_flushing=(period)
|
25
|
+
@auto_flushing[Thread.current] =
|
26
|
+
case period
|
27
|
+
when true; 1
|
28
|
+
when false, nil, 0; MAX_BUFFER_SIZE
|
29
|
+
when Integer; period
|
30
|
+
else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def flush
|
35
|
+
@guard.synchronize do
|
36
|
+
buffer.each do |content|
|
37
|
+
@log.write(content)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Important to do this even if buffer was empty or else @buffer will
|
41
|
+
# accumulate empty arrays for each request where nothing was logged.
|
42
|
+
clear_buffer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
flush
|
48
|
+
@log.close if @log.respond_to?(:close)
|
49
|
+
@log = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def auto_flush
|
55
|
+
flush if buffer.size >= auto_flushing
|
56
|
+
end
|
57
|
+
|
58
|
+
def buffer
|
59
|
+
@buffer[Thread.current]
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear_buffer
|
63
|
+
@buffer.delete(Thread.current)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module BufferedLogger::Formatting
|
4
|
+
DEFAULT_LEVEL = :default
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@formatter = BufferedLogger::ThreadHash.new { |h,k| h[k] = {} }
|
10
|
+
super()
|
11
|
+
end
|
12
|
+
|
13
|
+
def formatter(severity = DEFAULT_LEVEL)
|
14
|
+
@formatter[Thread.current][severity] || @formatter[master_thread][severity] || default_formatter
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_formatter(severity = DEFAULT_LEVEL, format = nil)
|
18
|
+
@formatter[Thread.current][severity] = BufferedLogger::Formatter.new(:format => format, :color => color?)
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_formatter
|
22
|
+
@formatter[master_thread][DEFAULT_LEVEL] ||= BufferedLogger::Formatter.new(:color => color?)
|
23
|
+
end
|
24
|
+
|
25
|
+
# magic format getters/setters
|
26
|
+
|
27
|
+
def method_missing(method, *args, &block)
|
28
|
+
case method.to_s
|
29
|
+
when /^(#{BufferedLogger::SEVERITY_LEVELS.join('|')})_formatter(=)?$/
|
30
|
+
$2 ? set_formatter($1.to_sym, args[0]) : formatter($1.to_sym)
|
31
|
+
else
|
32
|
+
super(method, *args, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
class BufferedLogger::Formatter
|
40
|
+
include ::Term::ANSIColor
|
41
|
+
|
42
|
+
FORMAT = "%s"
|
43
|
+
COLOR = true
|
44
|
+
|
45
|
+
def initialize(params = {})
|
46
|
+
@format = params[:format] || FORMAT
|
47
|
+
@color = params[:color] == false ? false : COLOR
|
48
|
+
end
|
49
|
+
|
50
|
+
# format accessors
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
@format
|
54
|
+
end
|
55
|
+
|
56
|
+
def format=(format)
|
57
|
+
@format = format.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
# color accessors
|
61
|
+
|
62
|
+
def color?
|
63
|
+
@color
|
64
|
+
end
|
65
|
+
|
66
|
+
def toggle_color
|
67
|
+
@color = !@color
|
68
|
+
end
|
69
|
+
|
70
|
+
# formatting
|
71
|
+
|
72
|
+
def %(message)
|
73
|
+
formatted_message = @format % message.to_s
|
74
|
+
color? ? parse_color(formatted_message) : formatted_message
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def parse_color(message)
|
80
|
+
color_methods = Term::ANSIColor.instance_methods
|
81
|
+
color_matcher = /#{color_methods.map {|m| "\\$#{m}\\s?"}.join('|')}/
|
82
|
+
|
83
|
+
strings = message.split(color_matcher)
|
84
|
+
colors = message.scan(color_matcher).map { |c| c[1..-1].strip }
|
85
|
+
|
86
|
+
colored_message = ''
|
87
|
+
strings[1..-1].each_with_index { |s,i| colored_message << self.send(colors[i], s) }
|
88
|
+
strings[0] + colored_message
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module BufferedLogger::Indentation
|
2
|
+
# padding is set at the thread level
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@padding = BufferedLogger::ThreadHash.new { |h,k| h[k] = BufferedLogger::Padding.new }
|
6
|
+
super()
|
7
|
+
end
|
8
|
+
|
9
|
+
def padding
|
10
|
+
@padding[Thread.current]
|
11
|
+
end
|
12
|
+
|
13
|
+
def indent(level, &block)
|
14
|
+
if block_given?
|
15
|
+
padding.indent(level)
|
16
|
+
ret_val = block.call
|
17
|
+
padding.indent(-level)
|
18
|
+
return ret_val
|
19
|
+
else
|
20
|
+
padding.indent(level)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
class BufferedLogger::Padding
|
27
|
+
PADDING_CHAR = ' '
|
28
|
+
PADDING_RESET= :reset
|
29
|
+
attr_reader :padding_char
|
30
|
+
|
31
|
+
def initialize(params = {})
|
32
|
+
@padding = ''
|
33
|
+
@padding_char = params[:padding_char] || PADDING_CHAR
|
34
|
+
indent(params[:indent] || 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
def padding_char=(char)
|
38
|
+
@padding_char = char.to_s[0..1]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
@padding
|
43
|
+
end
|
44
|
+
|
45
|
+
def indent(indent_level)
|
46
|
+
@padding = \
|
47
|
+
if indent_level == PADDING_RESET
|
48
|
+
''
|
49
|
+
elsif indent_level > 0
|
50
|
+
@padding + (@padding_char * indent_level)
|
51
|
+
else
|
52
|
+
@padding[0..(-1+indent_level)]
|
53
|
+
end
|
54
|
+
indent_level
|
55
|
+
end
|
56
|
+
|
57
|
+
def %(message)
|
58
|
+
@padding + message.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'active_support/core_ext/class'
|
3
|
+
|
4
|
+
class BufferedLogger
|
5
|
+
include Indentation
|
6
|
+
include Formatting
|
7
|
+
include Buffering
|
8
|
+
|
9
|
+
module Severity
|
10
|
+
DEBUG = 0
|
11
|
+
INFO = 1
|
12
|
+
WARN = 2
|
13
|
+
ERROR = 3
|
14
|
+
FATAL = 4
|
15
|
+
UNKNOWN = 5
|
16
|
+
end
|
17
|
+
include Severity
|
18
|
+
|
19
|
+
SEVERITY_LEVELS = Severity.constants.map { |c| c.downcase.to_sym }.freeze
|
20
|
+
SEVERITY_MAP = Severity.constants.inject({}) do |h,c|
|
21
|
+
h[c.downcase.to_sym] = Severity.const_get(c); h
|
22
|
+
end.freeze
|
23
|
+
|
24
|
+
|
25
|
+
##
|
26
|
+
# :singleton-method:
|
27
|
+
# Set to false to disable the silencer
|
28
|
+
cattr_accessor :silencer
|
29
|
+
self.silencer = true
|
30
|
+
|
31
|
+
attr_accessor :level
|
32
|
+
attr_reader :master_thread
|
33
|
+
private :master_thread
|
34
|
+
|
35
|
+
def initialize(log, level = DEBUG, params = {})
|
36
|
+
@master_thread = Thread.current
|
37
|
+
super()
|
38
|
+
|
39
|
+
@level = severity_to_const(level)
|
40
|
+
if log.respond_to?(:write)
|
41
|
+
@log = log
|
42
|
+
elsif File.exist?(log)
|
43
|
+
@log = open_log(log, (File::WRONLY | File::APPEND))
|
44
|
+
else
|
45
|
+
FileUtils.mkdir_p(File.dirname(log))
|
46
|
+
@log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
|
47
|
+
end
|
48
|
+
params.each { |k,v| SEVERITY_LEVELS.include?(k) ? set_formatter(k, v) : next }
|
49
|
+
end
|
50
|
+
|
51
|
+
def open_log(log, mode)
|
52
|
+
open(log, mode).tap do |open_log|
|
53
|
+
open_log.set_encoding(Encoding::BINARY) if open_log.respond_to?(:set_encoding)
|
54
|
+
open_log.sync = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add(severity, message = nil, progname = nil, &block)
|
59
|
+
return if @level > severity
|
60
|
+
message = (message || (block && block.call) || progname).to_s
|
61
|
+
# If a newline is necessary then create a new message ending with a newline.
|
62
|
+
# Ensures that the original message is not mutated.
|
63
|
+
message = "#{message}\n" unless message[-1] == ?\n
|
64
|
+
buffer << message
|
65
|
+
auto_flush
|
66
|
+
message
|
67
|
+
end
|
68
|
+
|
69
|
+
for severity in SEVERITY_LEVELS
|
70
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
71
|
+
def #{severity}(message = nil, progname = nil, &block)
|
72
|
+
add(#{SEVERITY_MAP[severity]}, padding % (formatter(:#{severity}) % message.to_s), progname, &block)
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def #{severity}?
|
77
|
+
#{SEVERITY_MAP[severity]} >= @level
|
78
|
+
end
|
79
|
+
EOT
|
80
|
+
end
|
81
|
+
|
82
|
+
# Silences the logger for the duration of the block.
|
83
|
+
def silence(temporary_level = ERROR)
|
84
|
+
if silencer
|
85
|
+
begin
|
86
|
+
old_logger_level, self.level = level, temporary_level
|
87
|
+
yield self
|
88
|
+
ensure
|
89
|
+
self.level = old_logger_level
|
90
|
+
end
|
91
|
+
else
|
92
|
+
yield self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def color?
|
99
|
+
@log == STDOUT
|
100
|
+
end
|
101
|
+
|
102
|
+
def severity_to_const(severity)
|
103
|
+
case severity
|
104
|
+
when Integer
|
105
|
+
severity
|
106
|
+
when Symbol
|
107
|
+
SEVERITY_MAP[severity]
|
108
|
+
else
|
109
|
+
severity.to_i
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
data/lib/buffered_logger.rb
CHANGED
@@ -1,58 +1,7 @@
|
|
1
|
-
|
2
|
-
require 'active_support/buffered_logger'
|
3
|
-
require 'forwardable'
|
1
|
+
class BufferedLogger; end
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
DEFAULT_FORMATTER = "%s"
|
11
|
-
DEFAULT_PADDING = ""
|
12
|
-
PADDING_CHAR = " "
|
13
|
-
|
14
|
-
def initialize(log_file, log_level, log_count = nil, log_size = nil)
|
15
|
-
@logger = ActiveSupport::BufferedLogger.new(log_file, log_level)
|
16
|
-
@padding, @formatter = {}, {}
|
17
|
-
end
|
18
|
-
|
19
|
-
def buffer
|
20
|
-
buf = @logger.send(:buffer)
|
21
|
-
buf && buf.join('')
|
22
|
-
end
|
23
|
-
|
24
|
-
# overwrite all the logging methods
|
25
|
-
class_eval do
|
26
|
-
[:debug, :info, :warn, :error, :fatal, :unknown].each do |method|
|
27
|
-
define_method(method) do |message|
|
28
|
-
@logger.send(method, (padding + formatter) % message.to_s)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def indent(indent_level)
|
34
|
-
@padding[Thread.current] = \
|
35
|
-
if indent_level == :reset
|
36
|
-
""
|
37
|
-
elsif indent_level > 0
|
38
|
-
padding + (PADDING_CHAR * indent_level)
|
39
|
-
else
|
40
|
-
padding[0..(-1+indent_level)]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def formatter=(format)
|
45
|
-
@formatter[Thread.current] = format
|
46
|
-
end
|
47
|
-
|
48
|
-
protected
|
49
|
-
|
50
|
-
def padding
|
51
|
-
@padding[Thread.current] ||= DEFAULT_PADDING
|
52
|
-
end
|
53
|
-
|
54
|
-
def formatter
|
55
|
-
@formatter[Thread.current] ||= DEFAULT_FORMATTER
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
3
|
+
require 'buffered_logger/thread_hash'
|
4
|
+
require 'buffered_logger/buffering'
|
5
|
+
require 'buffered_logger/indentation'
|
6
|
+
require 'buffered_logger/formatting'
|
7
|
+
require 'buffered_logger/logger'
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
2
|
+
|
3
|
+
describe BufferedLogger::Formatter do
|
4
|
+
before :each do
|
5
|
+
@f = BufferedLogger::Formatter.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'initialization and accessors' do
|
9
|
+
it "should init a new formatter object" do
|
10
|
+
@f.should be_an_instance_of BufferedLogger::Formatter
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return the default format string" do
|
14
|
+
@f.to_s.should == '%s'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set the format string" do
|
18
|
+
@f.format = '%d'
|
19
|
+
@f.to_s.should == '%d'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should check if color is enabled by default' do
|
23
|
+
@f.color?.should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should toggle the color setting' do
|
27
|
+
@f.toggle_color
|
28
|
+
@f.color?.should be_false
|
29
|
+
@f.toggle_color
|
30
|
+
@f.color?.should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should construct a formatter object based on params' do
|
34
|
+
f = BufferedLogger::Formatter.new(:format => '%s%s%s', :color => false)
|
35
|
+
f.to_s.should == '%s%s%s'
|
36
|
+
f.color?.should be_false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'formatting' do
|
41
|
+
it 'should parse color codes' do
|
42
|
+
s = "$blue blue $green green $red red"
|
43
|
+
@f.send(:parse_color, s).should == "\e[34mblue \e[0m\e[32mgreen \e[0m\e[31mred\e[0m"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should format an input string' do
|
47
|
+
s = 'text'
|
48
|
+
(@f % s).should == 'text'
|
49
|
+
@f.format = '### %s ###'
|
50
|
+
(@f % s).should == '### text ###'
|
51
|
+
@f.format = '$blue ### $red %s $blue ###'
|
52
|
+
(@f % s).should == "\e[34m### \e[0m\e[31mtext \e[0m\e[34m###\e[0m"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe BufferedLogger do
|
58
|
+
before :each do
|
59
|
+
@f = StringIO.new
|
60
|
+
@l = BufferedLogger.new(@f)
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'formatting' do
|
64
|
+
it 'should use appropriate formatting for each thread' do
|
65
|
+
@l.send(:set_formatter, :info, "$blue %s")
|
66
|
+
@l.info "oh"
|
67
|
+
|
68
|
+
t = Thread.new do
|
69
|
+
@l.send(:set_formatter, :info, "$red %s")
|
70
|
+
@l.info 'blah'
|
71
|
+
end; t.join
|
72
|
+
|
73
|
+
t = Thread.new do
|
74
|
+
@l.info 'hey'
|
75
|
+
end; t.join
|
76
|
+
|
77
|
+
@l.info "haha"
|
78
|
+
|
79
|
+
# colors are off so the keywords won't be parsed
|
80
|
+
@f.string.should == "$blue oh\n$red blah\n$blue hey\n$blue haha\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should use appropriate formatting for each severity level' do
|
84
|
+
@l.send(:set_formatter, :error, "$red %s")
|
85
|
+
@l.send(:set_formatter, :info, "$blue %s")
|
86
|
+
@l.error 'error'
|
87
|
+
@l.info 'info'
|
88
|
+
@f.string.should == "$red error\n$blue info\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should use the master thread formatter if one isnt set' do
|
92
|
+
@l.send(:set_formatter, :error, "$red %s")
|
93
|
+
@l.error 'test'
|
94
|
+
|
95
|
+
t = Thread.new do
|
96
|
+
@l.error 'blah'
|
97
|
+
end; t.join
|
98
|
+
@f.string.should == "$red test\n$red blah\n"
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should use the master thread default formatter if one isnt set' do
|
102
|
+
t_formatter = nil
|
103
|
+
t = Thread.new do
|
104
|
+
t_formatter = @l.send(:formatter, :info)
|
105
|
+
end; t.join
|
106
|
+
|
107
|
+
t_formatter.should == @l.send(:formatter, :info)
|
108
|
+
t_formatter.should == @l.send(:formatter, :error)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should allow syntactic sugar to get/set formatters' do
|
112
|
+
BufferedLogger::SEVERITY_LEVELS.each do |s|
|
113
|
+
@l.send("#{s}_formatter=", "$green #{s}: $white %s")
|
114
|
+
@l.send("#{s}_formatter").to_s.should == "$green #{s}: $white %s"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
2
|
+
|
3
|
+
describe BufferedLogger::Padding do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@p = BufferedLogger::Padding.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'initialization and accessors' do
|
10
|
+
it "should initialize a new padding object" do
|
11
|
+
@p.should be_an_instance_of BufferedLogger::Padding
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set padding character" do
|
15
|
+
@p.padding_char = '#'
|
16
|
+
@p.padding_char.should == '#'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return the padding string" do
|
20
|
+
@p.to_s.should == ''
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should construct pre-padded strings" do
|
24
|
+
p = BufferedLogger::Padding.new(:indent => 4, :padding_char => '#')
|
25
|
+
p.to_s.should == '####'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'indentation' do
|
30
|
+
it "should indent padding a positive amount" do
|
31
|
+
@p.indent(2)
|
32
|
+
@p.to_s.should == ' '
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should indent padding a negative amount" do
|
36
|
+
@p.indent(3)
|
37
|
+
@p.indent(-2)
|
38
|
+
@p.to_s.should == ' '
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should reset padding indentation" do
|
42
|
+
@p.indent(10)
|
43
|
+
@p.indent(:reset)
|
44
|
+
@p.to_s.should == ''
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'formatting' do
|
49
|
+
it "should apply padding to any input string" do
|
50
|
+
s = 'text'
|
51
|
+
@p.indent(4)
|
52
|
+
(@p % s).should == ' text'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe BufferedLogger do
|
60
|
+
before :each do
|
61
|
+
@f = StringIO.new
|
62
|
+
@l = BufferedLogger.new(@f)
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'indentation' do
|
66
|
+
it 'should use appropriate indentation for each thread' do
|
67
|
+
@l.indent(4)
|
68
|
+
@l.info 'hello'
|
69
|
+
|
70
|
+
t = Thread.new do
|
71
|
+
@l.indent(2)
|
72
|
+
@l.info 'blah'
|
73
|
+
end; t.join
|
74
|
+
|
75
|
+
t = Thread.new do
|
76
|
+
@l.info 'hey'
|
77
|
+
end; t.join
|
78
|
+
|
79
|
+
@l.info 'hi'
|
80
|
+
|
81
|
+
@f.string.should == " hello\n blah\nhey\n hi\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should temporarily indent any logging in an indent block' do
|
85
|
+
@l.indent(4) do
|
86
|
+
@l.info 'blah'
|
87
|
+
end
|
88
|
+
@l.info 'blah'
|
89
|
+
@f.string.should == " blah\nblah\n"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support/buffered_logger'
|
4
|
+
|
5
|
+
describe BufferedLogger do
|
6
|
+
before :each do
|
7
|
+
@f = StringIO.new
|
8
|
+
@l = BufferedLogger.new(@f)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'initialization and accessors' do
|
12
|
+
it 'should initialize a new logger object' do
|
13
|
+
@l.should be_an_instance_of BufferedLogger
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should initialize a logger object using a severity level symbol' do
|
17
|
+
l = BufferedLogger.new(@f, :info)
|
18
|
+
l.info "info"
|
19
|
+
l.debug "debug"
|
20
|
+
l.warn "warn"
|
21
|
+
@f.string.should == "info\nwarn\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should support color when printing to STDOUT' do
|
25
|
+
l = BufferedLogger.new(STDOUT)
|
26
|
+
l.send(:color?).should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not support color when not printing to STDOUT' do
|
30
|
+
@l.send(:color?).should == false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should autoset formatters during init' do
|
34
|
+
l = BufferedLogger.new(STDOUT, 0, {:info => '$green INFO: $white %s', :warn => '$yellow WARNING: $white %s', :error => '$red ERROR: $white %s'})
|
35
|
+
l.send(:formatter, :info).to_s.should == '$green INFO: $white %s'
|
36
|
+
l.send(:formatter, :warn).to_s.should == '$yellow WARNING: $white %s'
|
37
|
+
l.send(:formatter, :error).to_s.should == '$red ERROR: $white %s'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'delegation and magic methods' do
|
42
|
+
it 'should maintain backwards compatibility with buffered_logger interface' do
|
43
|
+
delegated = ActiveSupport::BufferedLogger.public_instance_methods(false)
|
44
|
+
delegated.each { |method| @l.should respond_to(method) }
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should respond to magic methods' do
|
48
|
+
magic = BufferedLogger::SEVERITY_LEVELS
|
49
|
+
magic.each { |method| @l.should respond_to(method) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'logging' do
|
54
|
+
it 'should log to a IO object' do
|
55
|
+
severities = BufferedLogger::SEVERITY_LEVELS
|
56
|
+
severities.each do |s|
|
57
|
+
f = StringIO.new
|
58
|
+
l = BufferedLogger.new(f)
|
59
|
+
l.send(s, "#{s}_message")
|
60
|
+
f.string.should == "#{s}_message\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should buffer unflushed statements' do
|
65
|
+
@l.auto_flushing = 0
|
66
|
+
@l.info 'test'
|
67
|
+
@l.info 'test2'
|
68
|
+
@l.buffer_text.should == "test\ntest2\n"
|
69
|
+
@f.string.should == ''
|
70
|
+
@l.flush
|
71
|
+
@f.string.should == "test\ntest2\n"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.2
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Alex Skryl
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-07-19 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: "0"
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
|
-
description:
|
35
|
+
description: A thread safe logger with formatting extensions. Based on active_support/buffered_logger.
|
36
36
|
email: rut216@gmail.com
|
37
37
|
executables: []
|
38
38
|
|
@@ -41,7 +41,19 @@ extensions: []
|
|
41
41
|
extra_rdoc_files: []
|
42
42
|
|
43
43
|
files:
|
44
|
+
- lib/buffered_logger/buffering.rb
|
45
|
+
- lib/buffered_logger/formatting.rb
|
46
|
+
- lib/buffered_logger/indentation.rb
|
47
|
+
- lib/buffered_logger/logger.rb
|
48
|
+
- lib/buffered_logger/thread_hash.rb
|
44
49
|
- lib/buffered_logger.rb
|
50
|
+
- spec/buffering_spec.rb
|
51
|
+
- spec/formatting_spec.rb
|
52
|
+
- spec/indentation_spec.rb
|
53
|
+
- spec/logger_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
- README
|
56
|
+
- TODO
|
45
57
|
has_rdoc: true
|
46
58
|
homepage: http://github.com/skryl
|
47
59
|
licenses: []
|
@@ -75,6 +87,6 @@ rubyforge_project:
|
|
75
87
|
rubygems_version: 1.6.2
|
76
88
|
signing_key:
|
77
89
|
specification_version: 3
|
78
|
-
summary:
|
90
|
+
summary: A flexible, thread safe logger with custom formatting and ANSI color support
|
79
91
|
test_files: []
|
80
92
|
|