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 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