yell 1.0.0 → 1.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/.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
|