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 CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
 
3
- script: "rake"
3
+ script: "rspec"
4
4
 
5
5
  rvm:
6
6
  - 1.8.7
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' ].sort.each do |file|
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
+
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'thread'
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
- @mutex = Mutex.new
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
- Metadata = lambda { |date, pattern| "# -*- #{date.iso8601} (#{date.to_f}) [#{pattern}] -*-" }
15
- MetadataRegexp = /^# -\*- (.+) \((\d+\.\d+)\) \[(.+)\] -\*-$/
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
- self.keep = options.fetch(:keep, 0)
23
-
24
- self.symlink = if options.key?(:symlink_original_filename)
25
- Yell._deprecate( "0.13.3", "Use :symlink for symlinking to oriinal filename",
26
- :before => "Yell.new { |l| l.adapter :datefile, :symlink_original_filename => true }",
27
- :after => "Yell.new { |l| l.adapter :datefile, :symlink => true }"
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
- return unless close? # do nothing when not closing
38
+ # do nothing when not closing
39
+ return unless close?
40
40
  close
41
41
 
42
- cleanup! if cleanup?
43
- symlink! if symlink?
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
- return if ::File.exist?( @filename ) # exit when file ready present
46
- stream.puts( Metadata.call(@date, date_pattern) )
48
+ symlink! if symlink?
49
+ cleanup! if cleanup?
47
50
  end
48
51
 
49
52
  close do
50
- @filename = filename_from( @date )
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
- # Cleanup old files
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" ) ].map do |f|
104
- [ f, metadata_from(f).last ]
105
- end.select do |(_, p)|
106
- date_pattern == p
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.map(&:first)[0..-keep] )
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
- def symlink?
125
- !!symlink
126
- end
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 filename_from( date )
159
+ def filename_for( date )
130
160
  @original_filename.sub( /(\.\w+)?$/, ".#{date.strftime(date_pattern)}\\1" )
131
161
  end
132
162
 
133
- def metadata_from( file )
134
- if m = ::File.open( file, &:readline ).match( MetadataRegexp )
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
- [ ::File.mtime( file ), "" ]
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
 
@@ -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 = true
34
+ @stream.sync = sync
25
35
 
26
36
  @stream
27
37
  end
@@ -20,8 +20,8 @@ module Yell #:nodoc:
20
20
  setup do |options|
21
21
  @stream = nil
22
22
 
23
- self.colors = options[:colors]
24
- self.format = options[:format]
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
- message << "\n" unless message[-1] == ?\n # add new line if there is none
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 message
26
- attr_reader :message
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, message = nil, options = {}, &block )
35
+ def initialize( level, *messages, &block )
39
36
  @time = Time.now
40
37
  @level = level
41
- @message = block ? block.call : message
42
- @options = options
38
+
39
+ @messages = messages
40
+ @messages << block.call if block
43
41
 
44
42
  @thread_id = Thread.current.object_id
45
43
 
@@ -61,8 +61,7 @@ module Yell #:nodoc:
61
61
  end
62
62
 
63
63
  PatternTable = {
64
- "m" => "message(event.message)", # Message
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 message( m )
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 m
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, &block )
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
- _call( &block ) if block
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 # def info?; @level.at?(1); end
132
- #
133
- def #{name}( m = nil, o = {}, &b ) # def info( m = nil, o = {}, &b )
134
- return false unless #{name}? # return false unless info?
135
- write Yell::Event.new(#{index}, m, o, &b) # write Yell::Event.new(1, m, o, &b)
136
- #
137
- true # true
138
- end # 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
  #
@@ -19,40 +19,50 @@ module Yell #:nodoc:
19
19
  @loggers = {}
20
20
  end
21
21
 
22
- # Set loggers in the repository
23
- #
24
- # @example Set a logger
25
- # Yell::Repository[ 'development' ] = Yell::Logger.new :stdout
26
- #
27
- # @return [Yell::Logger] The logger instance
28
- def self.[]=( name, logger )
29
- synchronize { instance.loggers[name] = logger }
30
- end
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
- if logger.nil? && name.respond_to?(:superclass)
44
- return Yell::Repository[ name.superclass ]
45
- end
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
- logger or raise Yell::LoggerNotFound.new(name)
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
- # Get the list of all loggers in the repository
52
+
53
+ # Fetch the logger by the given name.
52
54
  #
53
- # @return [Hash] The map of loggers
54
- def self.loggers
55
- synchronize { instance.loggers }
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
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Yell #:nodoc:
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
 
6
6
  end
7
7
 
@@ -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
- it "should write all messages from one instance" do
10
- logger = Yell.new( filename )
10
+ context "one instance" do
11
+ before do
12
+ logger = Yell.new filename
11
13
 
12
- (1..threads).map do |count|
13
- Thread.new { 10.times { logger.info count } }
14
- end.each(&:join)
14
+ range.map do |count|
15
+ Thread.new { 10.times { logger.info count } }
16
+ end.each(&:join)
15
17
 
16
- lines.should == 10*threads
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
- # it "should write all messages from multiple instances" do
20
- # (1..threads).map do |count|
21
- # logger = Yell.new( filename )
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
- # Thread.new do
24
- # 10.times { logger.info count }
25
- # end
26
- # end.each(&:join)
36
+ # sleep 0.5
37
+ # end
27
38
 
28
- # lines.should == 10*threads
39
+ # it "should write all messages" do
40
+ # lines.should == 10*threads
41
+ # end
29
42
  # end
30
43
 
31
- it "should write all messages from one repository" do
32
- Yell[ 'threaded' ] = Yell.new( filename )
44
+ context "one instance in the repository" do
45
+ before do
46
+ Yell[ 'threaded' ] = Yell.new( filename )
47
+ end
33
48
 
34
- (1..threads).map do |count|
35
- Thread.new { 10.times { Yell['threaded'].info count } }
36
- end.each(&:join)
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
- lines.should == 10*threads
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 ) { File.expand_path 'filename.log' }
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
@@ -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!', :test => :option }
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 :message do
47
- subject { event.message }
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
@@ -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!', :test => :option }
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" }
@@ -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 set" do
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.0.0
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-09-17 00:00:00.000000000 Z
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