yell 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/README.md +29 -0
- data/Rakefile +4 -30
- data/examples/006.2-the-loggable-module-with-inheritance.rb +41 -0
- data/lib/yell/adapters/base.rb +3 -7
- data/lib/yell/adapters/datefile.rb +70 -34
- data/lib/yell/adapters/file.rb +12 -2
- data/lib/yell/adapters/io.rb +15 -4
- data/lib/yell/event.rb +6 -8
- data/lib/yell/formatter.rb +16 -12
- data/lib/yell/logger.rb +10 -39
- data/lib/yell/repository.rb +38 -28
- data/lib/yell/version.rb +1 -1
- data/spec/threaded/yell_spec.rb +78 -20
- data/spec/yell/adapters/datefile_spec.rb +22 -0
- data/spec/yell/adapters/file_spec.rb +19 -1
- data/spec/yell/event_spec.rb +4 -9
- data/spec/yell/formatter_spec.rb +1 -6
- data/spec/yell/logger_spec.rb +31 -0
- data/spec/yell/repository_spec.rb +3 -1
- metadata +3 -2
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -107,6 +107,35 @@ Yell['mylog']
|
|
107
107
|
There is no need to define outputters separately and you don't have to taint
|
108
108
|
you global namespace with Yell's subclasses.
|
109
109
|
|
110
|
+
### You want any class to have a logger?
|
111
|
+
|
112
|
+
Yell comes with a simple module: +Yell::Loggable+. Simply include this in a class and
|
113
|
+
you are good to go.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# Before you can use it, you will need to define a logger and
|
117
|
+
# provide it with the `:name` of your class.
|
118
|
+
Yell.new :stdout, :name => 'Foo'
|
119
|
+
|
120
|
+
class Foo
|
121
|
+
include Yell::Loggable
|
122
|
+
end
|
123
|
+
|
124
|
+
# Now you can log
|
125
|
+
Foo.new.logger.info "Hello World"
|
126
|
+
```
|
127
|
+
|
128
|
+
It even works with class inheritance:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
# Given the above example, we inherit from Foo
|
132
|
+
class Bar < Foo
|
133
|
+
end
|
134
|
+
|
135
|
+
# The logger will fallback to the Foo superclass
|
136
|
+
Bar.new.logger.info "Hello World"
|
137
|
+
```
|
138
|
+
|
110
139
|
|
111
140
|
## Further Readings
|
112
141
|
|
data/Rakefile
CHANGED
@@ -1,42 +1,16 @@
|
|
1
1
|
$LOAD_PATH.unshift( 'lib' )
|
2
|
-
|
3
2
|
require 'bundler'
|
4
|
-
Bundler::GemHelper.install_tasks
|
5
3
|
|
4
|
+
|
5
|
+
# Run stuff in the examples folder
|
6
|
+
desc "Run examples"
|
6
7
|
task :examples do
|
7
8
|
require 'benchmark'
|
8
9
|
|
9
10
|
seconds = Benchmark.realtime do
|
10
|
-
Dir[ './examples/*.rb' ].
|
11
|
-
begin
|
12
|
-
puts "\n*** Running #{file}"
|
13
|
-
|
14
|
-
require file
|
15
|
-
rescue Exception => e
|
16
|
-
puts "#{e.class}: #{e.message}:\n\t#{e.backtrace.join("\n\t")}"
|
17
|
-
|
18
|
-
exit 1
|
19
|
-
end
|
20
|
-
end
|
11
|
+
Dir[ './examples/*.rb' ].each { |file| puts "\n\n=== Running #{file} ==="; require file }
|
21
12
|
end
|
22
13
|
|
23
14
|
puts "\n\t[ Examples took #{seconds} seconds to run ]"
|
24
15
|
end
|
25
16
|
|
26
|
-
# RSpec
|
27
|
-
begin
|
28
|
-
require 'rspec/core/rake_task'
|
29
|
-
|
30
|
-
desc "Run specs"
|
31
|
-
RSpec::Core::RakeTask.new do |t|
|
32
|
-
t.rspec_opts = %w(--color --format progress --order random)
|
33
|
-
# t.ruby_opts = %w(-w)
|
34
|
-
end
|
35
|
-
rescue LoadError
|
36
|
-
task :spec do
|
37
|
-
abort "`gem install rspec` in order to run tests"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
task :default => :spec
|
42
|
-
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative '../lib/yell'
|
4
|
+
|
5
|
+
puts <<-EOS
|
6
|
+
|
7
|
+
# You can add logging to any class by including the Yell::Loggable module.
|
8
|
+
#
|
9
|
+
# When including the module, your class will get a :logger method. Before you
|
10
|
+
# can use it, though, you will need to define a logger providing the :name of
|
11
|
+
# your class.
|
12
|
+
|
13
|
+
Yell.new :stdout, :name => 'Foo'
|
14
|
+
|
15
|
+
# Define the class
|
16
|
+
class Foo
|
17
|
+
include Yell::Loggable
|
18
|
+
end
|
19
|
+
|
20
|
+
class Bar < Foo; end
|
21
|
+
|
22
|
+
bar = Bar.new
|
23
|
+
bar.logger.info "Hello World!"
|
24
|
+
#=> "2012-02-29T09:30:00+01:00 [ INFO] 65784 : Hello World!"
|
25
|
+
|
26
|
+
|
27
|
+
EOS
|
28
|
+
|
29
|
+
puts "=== actual example ==="
|
30
|
+
|
31
|
+
Yell.new :stdout, :name => 'Foo'
|
32
|
+
|
33
|
+
class Foo
|
34
|
+
include Yell::Loggable
|
35
|
+
end
|
36
|
+
|
37
|
+
class Bar < Foo; end
|
38
|
+
|
39
|
+
bar = Bar.new
|
40
|
+
bar.logger.info "Hello World!"
|
41
|
+
|
data/lib/yell/adapters/base.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'monitor'
|
4
4
|
|
5
5
|
module Yell #:nodoc:
|
6
6
|
module Adapters #:nodoc:
|
@@ -40,7 +40,7 @@ module Yell #:nodoc:
|
|
40
40
|
#
|
41
41
|
# logger = Yell.new :puts
|
42
42
|
# logger.info "Hello World!"
|
43
|
-
class Base
|
43
|
+
class Base < Monitor
|
44
44
|
include Yell::Level::Helpers
|
45
45
|
|
46
46
|
class << self
|
@@ -125,7 +125,7 @@ module Yell #:nodoc:
|
|
125
125
|
#
|
126
126
|
# You should not overload the constructor, use #setup instead.
|
127
127
|
def initialize( options = {}, &block )
|
128
|
-
|
128
|
+
super() # init the monitor superclass
|
129
129
|
|
130
130
|
setup!(options)
|
131
131
|
block.call(self) if block
|
@@ -191,10 +191,6 @@ module Yell #:nodoc:
|
|
191
191
|
@level.nil? || @level.at?( event.level )
|
192
192
|
end
|
193
193
|
|
194
|
-
def synchronize( &block )
|
195
|
-
@mutex.synchronize( &block )
|
196
|
-
end
|
197
|
-
|
198
194
|
end
|
199
195
|
|
200
196
|
end
|
@@ -4,50 +4,53 @@ module Yell #:nodoc:
|
|
4
4
|
module Adapters #:nodoc:
|
5
5
|
|
6
6
|
# The +Datefile+ adapter is similar to the +File+ adapter. However, it
|
7
|
-
# rotates the file at midnight.
|
7
|
+
# rotates the file at midnight (by default).
|
8
8
|
class Datefile < Yell::Adapters::File
|
9
9
|
|
10
10
|
# The default date pattern, e.g. "19820114" (14 Jan 1982)
|
11
11
|
DefaultDatePattern = "%Y%m%d"
|
12
12
|
|
13
13
|
# Metadata
|
14
|
-
|
15
|
-
|
14
|
+
Header = lambda { |date, pattern| "# -*- #{date.iso8601} (#{date.to_f}) [#{pattern}] -*-" }
|
15
|
+
HeaderRegexp = /^# -\*- (.+) \((\d+\.\d+)\) \[(.+)\] -\*-$/
|
16
16
|
|
17
17
|
|
18
18
|
setup do |options|
|
19
19
|
@date, @date_strftime = nil, nil # default; do not override --R
|
20
20
|
|
21
|
+
# check whether to write the log header (default true)
|
22
|
+
self.header = options.fetch(:header, true)
|
23
|
+
|
24
|
+
# check the date pattern on the filename (default "%Y%m%d")
|
21
25
|
self.date_pattern = options.fetch(:date_pattern, DefaultDatePattern)
|
22
|
-
|
23
|
-
|
24
|
-
self.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
)
|
29
|
-
options.fetch(:symlink_original_filename, false)
|
30
|
-
else
|
31
|
-
options.fetch(:symlink, false)
|
32
|
-
end
|
26
|
+
|
27
|
+
# check whether to cleanup old files of the same pattern (default false)
|
28
|
+
self.keep = options.fetch(:keep, false)
|
29
|
+
|
30
|
+
# check whether to symlink the otiginal filename (default false)
|
31
|
+
self.symlink = options.fetch(:symlink, false)
|
33
32
|
|
34
33
|
@original_filename = ::File.expand_path options.fetch(:filename, default_filename)
|
35
34
|
options[:filename] = @original_filename
|
36
35
|
end
|
37
36
|
|
38
37
|
write do |event|
|
39
|
-
|
38
|
+
# do nothing when not closing
|
39
|
+
return unless close?
|
40
40
|
close
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# exit when file ready present
|
43
|
+
return if ::File.exist?( @filename )
|
44
|
+
|
45
|
+
# write the header if applicable
|
46
|
+
stream.puts( Header.call(@date, date_pattern) ) if header?
|
44
47
|
|
45
|
-
|
46
|
-
|
48
|
+
symlink! if symlink?
|
49
|
+
cleanup! if cleanup?
|
47
50
|
end
|
48
51
|
|
49
52
|
close do
|
50
|
-
@filename =
|
53
|
+
@filename = filename_for( @date )
|
51
54
|
end
|
52
55
|
|
53
56
|
|
@@ -77,6 +80,15 @@ module Yell #:nodoc:
|
|
77
80
|
# keep = 0
|
78
81
|
attr_accessor :keep
|
79
82
|
|
83
|
+
# You can suppress the first line of the logfile that contains
|
84
|
+
# the metadata. This is important upon rollover, because on *nix
|
85
|
+
# systems, it is not possible to determine the creation time of a file,
|
86
|
+
# on the last access time. The header compensates this.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# header = false
|
90
|
+
attr_accessor :header
|
91
|
+
|
80
92
|
|
81
93
|
private
|
82
94
|
|
@@ -92,27 +104,39 @@ module Yell #:nodoc:
|
|
92
104
|
|
93
105
|
if @stream.nil? or _date_strftime != @date_strftime
|
94
106
|
@date, @date_strftime = _date, _date_strftime
|
107
|
+
|
95
108
|
return true
|
96
109
|
end
|
97
110
|
|
98
111
|
false
|
99
112
|
end
|
100
113
|
|
101
|
-
#
|
114
|
+
# Removes old logfiles of the same date pattern.
|
115
|
+
#
|
116
|
+
# By reading the header of the files that match the date pattern, the
|
117
|
+
# adapter determines whether to remove them or not. If no header is present,
|
118
|
+
# it makes the best guess by checking the last access time (which may result
|
119
|
+
# in false cleanups).
|
102
120
|
def cleanup!
|
103
|
-
files = Dir[ @original_filename.sub( /(\.\w+)?$/, ".*\\1" ) ].
|
104
|
-
|
105
|
-
|
106
|
-
|
121
|
+
files = Dir[ @original_filename.sub( /(\.\w+)?$/, ".*\\1" ) ].sort.select do |file|
|
122
|
+
created, pattern = header_from(file)
|
123
|
+
|
124
|
+
# Select if the date pattern is nil (no header info available within the file) or
|
125
|
+
# when the pattern matches.
|
126
|
+
pattern.nil? || pattern == self.date_pattern
|
107
127
|
end
|
108
128
|
|
109
|
-
::File.unlink( *files
|
129
|
+
::File.unlink( *files[0..-keep-1] )
|
110
130
|
end
|
111
131
|
|
132
|
+
# Cleanup old logfiles?
|
133
|
+
#
|
134
|
+
# @return [Boolean] true or false
|
112
135
|
def cleanup?
|
113
|
-
keep.to_i > 0
|
136
|
+
!!keep && keep.to_i > 0
|
114
137
|
end
|
115
138
|
|
139
|
+
# Symlink the current filename to the original one.
|
116
140
|
def symlink!
|
117
141
|
# do nothing, because symlink is already correct
|
118
142
|
return if ::File.symlink?(@original_filename) && ::File.readlink(@original_filename) == @filename
|
@@ -121,20 +145,32 @@ module Yell #:nodoc:
|
|
121
145
|
::File.symlink( @filename, @original_filename )
|
122
146
|
end
|
123
147
|
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
# Symlink the original filename?
|
149
|
+
#
|
150
|
+
# @return [Boolean] true or false
|
151
|
+
def symlink?; !!symlink; end
|
152
|
+
|
153
|
+
# Write header into the file?
|
154
|
+
#
|
155
|
+
# @return [Boolean] true or false
|
156
|
+
def header?; !!header; end
|
127
157
|
|
128
158
|
# Sets the filename with the `:date_pattern` appended to it.
|
129
|
-
def
|
159
|
+
def filename_for( date )
|
130
160
|
@original_filename.sub( /(\.\w+)?$/, ".#{date.strftime(date_pattern)}\\1" )
|
131
161
|
end
|
132
162
|
|
133
|
-
|
134
|
-
|
163
|
+
# Fetch the header form the file
|
164
|
+
def header_from( file )
|
165
|
+
if m = ::File.open( file, &:readline ).match( HeaderRegexp )
|
166
|
+
# in case there is a Header present, we can just read from it
|
135
167
|
[ Time.at( m[2].to_f ), m[3] ]
|
136
168
|
else
|
137
|
-
|
169
|
+
# In case there is no header: we need to take a good guess
|
170
|
+
#
|
171
|
+
# Since the pattern can not be determined, we will just return the Posix ctime.
|
172
|
+
# That is NOT the creatint time, so the value will potentially be wrong!
|
173
|
+
[ ::File.ctime(file), nil ]
|
138
174
|
end
|
139
175
|
end
|
140
176
|
|
data/lib/yell/adapters/file.rb
CHANGED
@@ -9,19 +9,29 @@ module Yell #:nodoc:
|
|
9
9
|
|
10
10
|
setup do |options|
|
11
11
|
@filename = ::File.expand_path options.fetch(:filename, default_filename)
|
12
|
+
|
13
|
+
# sync immediately to IO (or not)
|
14
|
+
self.sync = options.fetch(:sync, true)
|
12
15
|
end
|
13
16
|
|
14
17
|
|
18
|
+
# Sets the “sync mode” to true or false.
|
19
|
+
#
|
20
|
+
# When true (default), every log event is immediately written to the file.
|
21
|
+
# When false, the log event is buffered internally.
|
22
|
+
attr_accessor :sync
|
23
|
+
|
24
|
+
|
15
25
|
private
|
16
26
|
|
17
27
|
# @overload Lazily open the file handle
|
18
28
|
def stream
|
19
|
-
@stream or open!
|
29
|
+
synchronize { @stream or open! }
|
20
30
|
end
|
21
31
|
|
22
32
|
def open!
|
23
33
|
@stream = ::File.open( @filename, ::File::WRONLY|::File::APPEND|::File::CREAT )
|
24
|
-
@stream.sync =
|
34
|
+
@stream.sync = sync
|
25
35
|
|
26
36
|
@stream
|
27
37
|
end
|
data/lib/yell/adapters/io.rb
CHANGED
@@ -20,8 +20,8 @@ module Yell #:nodoc:
|
|
20
20
|
setup do |options|
|
21
21
|
@stream = nil
|
22
22
|
|
23
|
-
self.colors = options
|
24
|
-
self.format = options
|
23
|
+
self.colors = options.fetch(:colors, false)
|
24
|
+
self.format = options.fetch(:format, nil)
|
25
25
|
end
|
26
26
|
|
27
27
|
write do |event|
|
@@ -32,7 +32,8 @@ module Yell #:nodoc:
|
|
32
32
|
message = color + message + Colors[-1]
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
# add new line if there is none
|
36
|
+
message << "\n" unless message[-1] == ?\n
|
36
37
|
|
37
38
|
stream.write( message )
|
38
39
|
end
|
@@ -43,9 +44,19 @@ module Yell #:nodoc:
|
|
43
44
|
end
|
44
45
|
|
45
46
|
|
47
|
+
# Sets colored output on or off (default off)
|
48
|
+
#
|
49
|
+
# @example Enable colors
|
50
|
+
# colors = true
|
51
|
+
#
|
52
|
+
# @example Disable colors
|
53
|
+
# colors = false
|
46
54
|
attr_accessor :colors
|
47
55
|
|
48
|
-
# Shortcut to enable colors
|
56
|
+
# Shortcut to enable colors.
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# colorize!
|
49
60
|
def colorize!; @colors = true; end
|
50
61
|
|
51
62
|
|
data/lib/yell/event.rb
CHANGED
@@ -22,11 +22,8 @@ module Yell #:nodoc:
|
|
22
22
|
# Accessor to the log level
|
23
23
|
attr_reader :level
|
24
24
|
|
25
|
-
# Accessor to the log
|
26
|
-
attr_reader :
|
27
|
-
|
28
|
-
# Accessor to additional options
|
29
|
-
attr_reader :options
|
25
|
+
# Accessor to the log messages
|
26
|
+
attr_reader :messages
|
30
27
|
|
31
28
|
# Accessor to the time the log event occured
|
32
29
|
attr_reader :time
|
@@ -35,11 +32,12 @@ module Yell #:nodoc:
|
|
35
32
|
attr_reader :thread_id
|
36
33
|
|
37
34
|
|
38
|
-
def initialize( level,
|
35
|
+
def initialize( level, *messages, &block )
|
39
36
|
@time = Time.now
|
40
37
|
@level = level
|
41
|
-
|
42
|
-
@
|
38
|
+
|
39
|
+
@messages = messages
|
40
|
+
@messages << block.call if block
|
43
41
|
|
44
42
|
@thread_id = Thread.current.object_id
|
45
43
|
|
data/lib/yell/formatter.rb
CHANGED
@@ -61,8 +61,7 @@ module Yell #:nodoc:
|
|
61
61
|
end
|
62
62
|
|
63
63
|
PatternTable = {
|
64
|
-
"m" => "message(event.
|
65
|
-
"o" => "message(event.options)", # Message options
|
64
|
+
"m" => "message(*event.messages)", # Message
|
66
65
|
"l" => "level(event.level)[0,1]", # Level (short), e.g.'I', 'W'
|
67
66
|
"L" => "level(event.level)", # Level, e.g. 'INFO', 'WARN'
|
68
67
|
"d" => "date(event.time)", # ISO8601 Timestamp
|
@@ -116,7 +115,19 @@ module Yell #:nodoc:
|
|
116
115
|
-
|
117
116
|
end
|
118
117
|
|
119
|
-
def
|
118
|
+
def level( l )
|
119
|
+
Yell::Severities[ l ]
|
120
|
+
end
|
121
|
+
|
122
|
+
def date( t )
|
123
|
+
@date_pattern ? t.strftime( @date_pattern ) : t.iso8601
|
124
|
+
end
|
125
|
+
|
126
|
+
def message( *messages )
|
127
|
+
messages.map { |m| to_message(m) }.join(" ")
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_message( m )
|
120
131
|
case m
|
121
132
|
when Hash
|
122
133
|
m.map { |k,v| "#{k}: #{v}" }.join( ", " )
|
@@ -124,18 +135,11 @@ module Yell #:nodoc:
|
|
124
135
|
backtrace = m.backtrace ? "\n\t#{m.backtrace.join("\n\t")}" : ""
|
125
136
|
|
126
137
|
"%s: %s%s" % [m.class, m.message, backtrace]
|
127
|
-
else
|
138
|
+
else
|
139
|
+
m
|
128
140
|
end
|
129
141
|
end
|
130
142
|
|
131
|
-
def level( l )
|
132
|
-
Yell::Severities[ l ]
|
133
|
-
end
|
134
|
-
|
135
|
-
def date( t )
|
136
|
-
@date_pattern ? t.strftime( @date_pattern ) : t.iso8601
|
137
|
-
end
|
138
|
-
|
139
143
|
end
|
140
144
|
end
|
141
145
|
|
data/lib/yell/logger.rb
CHANGED
@@ -34,7 +34,7 @@ module Yell #:nodoc:
|
|
34
34
|
# Yell::Logger.new :datefile, 'development.log' do |l|
|
35
35
|
# l.level = :info
|
36
36
|
# end
|
37
|
-
def initialize( *args
|
37
|
+
def initialize( *args )
|
38
38
|
@adapters = []
|
39
39
|
|
40
40
|
# extract options
|
@@ -58,7 +58,7 @@ module Yell #:nodoc:
|
|
58
58
|
self.adapter args.pop if args.any?
|
59
59
|
|
60
60
|
# eval the given block
|
61
|
-
|
61
|
+
yield(self) if block_given?
|
62
62
|
|
63
63
|
# default adapter when none defined
|
64
64
|
self.adapter :file if @adapters.empty?
|
@@ -98,21 +98,6 @@ module Yell #:nodoc:
|
|
98
98
|
Yell::Repository[val] = self
|
99
99
|
end
|
100
100
|
|
101
|
-
# Deprecated: Use attr_reader in future
|
102
|
-
def level( val = nil )
|
103
|
-
if val.nil?
|
104
|
-
@level
|
105
|
-
else
|
106
|
-
# deprecated, but should still work
|
107
|
-
Yell._deprecate( "0.5.0", "Use :level= for setting the log level",
|
108
|
-
:before => "Yell::Logger.new { level :info }",
|
109
|
-
:after => "Yell::Logger.new { |l| l.level = :info }"
|
110
|
-
)
|
111
|
-
|
112
|
-
@level = Yell::Level.new( val )
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
101
|
# Convenience method for resetting all adapters of the Logger.
|
117
102
|
def close
|
118
103
|
@adapters.each(&:close)
|
@@ -128,34 +113,20 @@ module Yell #:nodoc:
|
|
128
113
|
name = s.downcase
|
129
114
|
|
130
115
|
class_eval <<-EOS, __FILE__, __LINE__
|
131
|
-
def #{name}?; @level.at?(#{index}); end
|
132
|
-
|
133
|
-
def #{name}( m
|
134
|
-
return false unless #{name}?
|
135
|
-
write Yell::Event.new(#{index}, m,
|
136
|
-
|
137
|
-
true
|
138
|
-
end
|
116
|
+
def #{name}?; @level.at?(#{index}); end # def info?; @level.at?(1); end
|
117
|
+
#
|
118
|
+
def #{name}( *m, &b ) # def info( *m, &b )
|
119
|
+
return false unless #{name}? # return false unless info?
|
120
|
+
write Yell::Event.new(#{index}, *m, &b) # write Yell::Event.new(1, *m, &b)
|
121
|
+
#
|
122
|
+
true # true
|
123
|
+
end # end
|
139
124
|
EOS
|
140
125
|
end
|
141
126
|
|
142
127
|
|
143
128
|
private
|
144
129
|
|
145
|
-
def _call( &block )
|
146
|
-
if block.arity == 0
|
147
|
-
Yell._deprecate( "0.5.0", "Yell::Logger.new with block expects argument now",
|
148
|
-
:before => "Yell::Logger.new { adapter STDOUT }",
|
149
|
-
:after => "Yell::Logger.new { |l| l.adapter STDOUT }"
|
150
|
-
)
|
151
|
-
|
152
|
-
# deprecated, but should still work
|
153
|
-
instance_eval( &block )
|
154
|
-
else
|
155
|
-
block.call(self)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
130
|
# The :adapters key may be passed to the options hash. It may appear in
|
160
131
|
# multiple variations:
|
161
132
|
#
|
data/lib/yell/repository.rb
CHANGED
@@ -19,40 +19,50 @@ module Yell #:nodoc:
|
|
19
19
|
@loggers = {}
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Get loggers from the repository
|
33
|
-
#
|
34
|
-
# @example Get the logger
|
35
|
-
# Yell::Repository[ 'development' ]
|
36
|
-
#
|
37
|
-
# @raise [Yell::LoggerNotFound] Raised when repository does not have that key
|
38
|
-
# @return [Yell::Logger] The logger instance
|
39
|
-
def self.[]( name )
|
40
|
-
synchronize do
|
41
|
-
logger = instance.loggers[name] || instance.loggers[name.to_s]
|
22
|
+
class << self
|
23
|
+
# Set loggers in the repository
|
24
|
+
#
|
25
|
+
# @example Set a logger
|
26
|
+
# Yell::Repository[ 'development' ] = Yell::Logger.new :stdout
|
27
|
+
#
|
28
|
+
# @return [Yell::Logger] The logger instance
|
29
|
+
def []=( name, logger )
|
30
|
+
synchronize { instance.loggers[name] = logger }
|
31
|
+
end
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
33
|
+
# Get loggers from the repository
|
34
|
+
#
|
35
|
+
# @example Get the logger
|
36
|
+
# Yell::Repository[ 'development' ]
|
37
|
+
#
|
38
|
+
# @raise [Yell::LoggerNotFound] Raised when repository does not have that key
|
39
|
+
# @return [Yell::Logger] The logger instance
|
40
|
+
def []( name )
|
41
|
+
synchronize { instance.fetch(name) or raise Yell::LoggerNotFound.new(name) }
|
42
|
+
end
|
46
43
|
|
47
|
-
|
44
|
+
# Get the list of all loggers in the repository
|
45
|
+
#
|
46
|
+
# @return [Hash] The map of loggers
|
47
|
+
def loggers
|
48
|
+
synchronize { instance.loggers }
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
|
52
|
+
|
53
|
+
# Fetch the logger by the given name.
|
52
54
|
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
55
|
+
# If the logger could not be found and has a superclass, it
|
56
|
+
# will attempt to look there. This is important for the
|
57
|
+
# Yell::Loggable module.
|
58
|
+
def fetch( name )
|
59
|
+
logger = loggers[name] || loggers[name.to_s]
|
60
|
+
|
61
|
+
if logger.nil? && name.respond_to?(:superclass)
|
62
|
+
return fetch( name.superclass )
|
63
|
+
end
|
64
|
+
|
65
|
+
logger
|
56
66
|
end
|
57
67
|
|
58
68
|
end
|
data/lib/yell/version.rb
CHANGED
data/spec/threaded/yell_spec.rb
CHANGED
@@ -2,41 +2,99 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "running Yell multi-threaded" do
|
4
4
|
let( :threads ) { 100 }
|
5
|
+
let( :range ) { (1..threads) }
|
5
6
|
|
6
7
|
let( :filename ) { fixture_path + '/threaded.log' }
|
7
8
|
let( :lines ) { `wc -l #{filename}`.to_i }
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
context "one instance" do
|
11
|
+
before do
|
12
|
+
logger = Yell.new filename
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
range.map do |count|
|
15
|
+
Thread.new { 10.times { logger.info count } }
|
16
|
+
end.each(&:join)
|
15
17
|
|
16
|
-
|
18
|
+
sleep 0.5
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should write all messages" do
|
22
|
+
lines.should == 10*threads
|
23
|
+
end
|
17
24
|
end
|
18
25
|
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
26
|
+
# context "one instance per thread" do
|
27
|
+
# before do
|
28
|
+
# range.map do |count|
|
29
|
+
# Thread.new do
|
30
|
+
# logger = Yell.new( filename )
|
31
|
+
|
32
|
+
# 10.times { logger.info count }
|
33
|
+
# end
|
34
|
+
# end.each(&:join)
|
22
35
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# end
|
26
|
-
# end.each(&:join)
|
36
|
+
# sleep 0.5
|
37
|
+
# end
|
27
38
|
|
28
|
-
#
|
39
|
+
# it "should write all messages" do
|
40
|
+
# lines.should == 10*threads
|
41
|
+
# end
|
29
42
|
# end
|
30
43
|
|
31
|
-
|
32
|
-
|
44
|
+
context "one instance in the repository" do
|
45
|
+
before do
|
46
|
+
Yell[ 'threaded' ] = Yell.new( filename )
|
47
|
+
end
|
33
48
|
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
it "should write all messages" do
|
50
|
+
range.map do |count|
|
51
|
+
Thread.new { 10.times { Yell['threaded'].info count } }
|
52
|
+
end.each(&:join)
|
37
53
|
|
38
|
-
|
54
|
+
lines.should == 10*threads
|
55
|
+
end
|
39
56
|
end
|
40
57
|
|
58
|
+
context "multiple datefile instances" do
|
59
|
+
let( :threadlist ) { [] }
|
60
|
+
let( :date ) { Time.now }
|
61
|
+
|
62
|
+
before do
|
63
|
+
Timecop.freeze( date - 86400 )
|
64
|
+
|
65
|
+
range.each do |count|
|
66
|
+
threadlist << Thread.new do
|
67
|
+
logger = Yell.new :datefile, :filename => filename, :keep => 2
|
68
|
+
loop { logger.info :info; sleep 0.1 }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
sleep 0.3 # sleep to get some messages into the file
|
73
|
+
end
|
74
|
+
|
75
|
+
after do
|
76
|
+
threadlist.each(&:kill)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should safely rollover" do
|
80
|
+
# now cycle the days
|
81
|
+
7.times do |count|
|
82
|
+
Timecop.freeze( date + 86400*count )
|
83
|
+
sleep 0.3
|
84
|
+
|
85
|
+
files = Dir[ fixture_path + '/*.log' ]
|
86
|
+
files.size.should == 2
|
87
|
+
|
88
|
+
# files.last.should match( datefile_pattern_for(Time.now) ) # today
|
89
|
+
# files.first.should match( datefile_pattern_for(Time.now-86400) ) # yesterday
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def datefile_pattern_for( time )
|
97
|
+
time.strftime(Yell::Adapters::Datefile::DefaultDatePattern)
|
98
|
+
end
|
41
99
|
end
|
42
100
|
|
@@ -80,6 +80,28 @@ describe Yell::Adapters::Datefile do
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
+
describe :header do
|
84
|
+
let( :adapter ) { Yell::Adapters::Datefile.new(:filename => filename) }
|
85
|
+
let( :header ) { File.open(datefile_filename, &:readline) }
|
86
|
+
|
87
|
+
before do
|
88
|
+
adapter.format = "%m" # easier to parse
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should be written by default" do
|
92
|
+
adapter.write( event )
|
93
|
+
|
94
|
+
header.should match(Yell::Adapters::Datefile::HeaderRegexp)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not be written when false" do
|
98
|
+
adapter.header = false
|
99
|
+
adapter.write( event )
|
100
|
+
|
101
|
+
header.should == "Hello World\n"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
83
105
|
|
84
106
|
private
|
85
107
|
|
@@ -30,7 +30,7 @@ describe Yell::Adapters::File do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
context "with given filename" do
|
33
|
-
let( :filename ) {
|
33
|
+
let( :filename ) { fixture_path + '/filename.log' }
|
34
34
|
let( :adapter ) { Yell::Adapters::File.new( :filename => filename ) }
|
35
35
|
|
36
36
|
it "should print to file" do
|
@@ -39,6 +39,24 @@ describe Yell::Adapters::File do
|
|
39
39
|
adapter.write( event )
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
context :sync do
|
44
|
+
let( :adapter ) { Yell::Adapters::File.new }
|
45
|
+
|
46
|
+
it "should sync by default" do
|
47
|
+
mock( devnull ).sync=( true )
|
48
|
+
|
49
|
+
adapter.write( event )
|
50
|
+
end
|
51
|
+
|
52
|
+
it "pass the option to File" do
|
53
|
+
adapter.sync = false
|
54
|
+
|
55
|
+
mock( devnull ).sync=( false )
|
56
|
+
|
57
|
+
adapter.write( event )
|
58
|
+
end
|
59
|
+
end
|
42
60
|
end
|
43
61
|
|
44
62
|
end
|
data/spec/yell/event_spec.rb
CHANGED
@@ -17,7 +17,7 @@ class EventFactory
|
|
17
17
|
end
|
18
18
|
|
19
19
|
describe Yell::Event do
|
20
|
-
let(:event) { Yell::Event.new 1, 'Hello World!'
|
20
|
+
let(:event) { Yell::Event.new 1, 'Hello World!' }
|
21
21
|
|
22
22
|
context :caller do
|
23
23
|
let( :event ) { EventFactory.event 1, "Hello World" }
|
@@ -43,14 +43,9 @@ describe Yell::Event do
|
|
43
43
|
it { should == 1 }
|
44
44
|
end
|
45
45
|
|
46
|
-
context :
|
47
|
-
subject { event.
|
48
|
-
it { should == 'Hello World!' }
|
49
|
-
end
|
50
|
-
|
51
|
-
context :options do
|
52
|
-
subject { event.options }
|
53
|
-
it { should == { :test => :option } }
|
46
|
+
context :messages do
|
47
|
+
subject { event.messages }
|
48
|
+
it { should == ['Hello World!'] }
|
54
49
|
end
|
55
50
|
|
56
51
|
context :time do
|
data/spec/yell/formatter_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Yell::Formatter do
|
4
4
|
|
5
5
|
let( :formatter ) { Yell::Formatter.new(subject) }
|
6
|
-
let( :event ) { Yell::Event.new 1, 'Hello World!'
|
6
|
+
let( :event ) { Yell::Event.new 1, 'Hello World!' }
|
7
7
|
let( :time ) { Time.now }
|
8
8
|
|
9
9
|
let( :format ) { formatter.format(event) }
|
@@ -17,11 +17,6 @@ describe Yell::Formatter do
|
|
17
17
|
it { format.should == "Hello World!" }
|
18
18
|
end
|
19
19
|
|
20
|
-
context "%o" do
|
21
|
-
subject { "%o" }
|
22
|
-
it { format.should == "test: option" }
|
23
|
-
end
|
24
|
-
|
25
20
|
context "%l" do
|
26
21
|
subject { "%l" }
|
27
22
|
it { format.should == "I" }
|
data/spec/yell/logger_spec.rb
CHANGED
@@ -13,6 +13,7 @@ class LoggerFactory
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe Yell::Logger do
|
16
|
+
let( :filename ) { fixture_path + '/logger.log' }
|
16
17
|
|
17
18
|
context "a Logger instance" do
|
18
19
|
let( :logger ) { Yell::Logger.new }
|
@@ -155,5 +156,35 @@ describe Yell::Logger do
|
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
159
|
+
context "logging" do
|
160
|
+
let( :logger ) { Yell::Logger.new(filename, :format => "%m") }
|
161
|
+
let( :line ) { File.open(filename, &:readline) }
|
162
|
+
|
163
|
+
it "should output a single message" do
|
164
|
+
logger.info "Hello World"
|
165
|
+
line.should == "Hello World\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should output multiple messages" do
|
169
|
+
logger.info "Hello", "W", "o", "r", "l", "d"
|
170
|
+
line.should == "Hello W o r l d\n"
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should output a hash and message" do
|
174
|
+
logger.info "Hello World", :test => :message
|
175
|
+
line.should == "Hello World test: message\n"
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should output a hash and message" do
|
179
|
+
logger.info( {:test => :message}, "Hello World" )
|
180
|
+
line.should == "test: message Hello World\n"
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should output a hash and block" do
|
184
|
+
logger.info(:test => :message) { "Hello World" }
|
185
|
+
line.should == "test: message Hello World\n"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
158
189
|
end
|
159
190
|
|
@@ -23,7 +23,9 @@ describe Yell::Repository do
|
|
23
23
|
@logger = Yell.new :stdout, :name => "Numeric"
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should raise when not
|
26
|
+
it "should raise with the correct :name when logger not found" do
|
27
|
+
mock.proxy( Yell::LoggerNotFound ).new( String )
|
28
|
+
|
27
29
|
lambda { Yell::Repository[ String ] }.should raise_error( Yell::LoggerNotFound )
|
28
30
|
end
|
29
31
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-03 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Yell - Your Extensible Logging Library. Define multiple adapters, various
|
15
15
|
log level combinations or message formatting options like you've never done before
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- examples/004.1-colorizing-the-log-output.rb
|
36
36
|
- examples/005.1-repository.rb
|
37
37
|
- examples/006.1-the-loggable-module.rb
|
38
|
+
- examples/006.2-the-loggable-module-with-inheritance.rb
|
38
39
|
- lib/yell.rb
|
39
40
|
- lib/yell/adapters.rb
|
40
41
|
- lib/yell/adapters/base.rb
|