yell 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -7,6 +7,6 @@ gemspec
7
7
  group :development do
8
8
  gem "rake"
9
9
 
10
- gem "rspec", "~> 2"
10
+ gem "rspec"
11
11
  gem "timecop"
12
- end
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011-2012 Rudolf Schmidt
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ Yell - Your Extensible Logging Library
2
+
3
+ ## Installation
4
+
5
+ System wide:
6
+
7
+ ```console
8
+ gem install yell
9
+ ```
10
+
11
+ Or in your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'yell'
15
+ ```
16
+
17
+ ## Usage
18
+ On the basics, Yell works just like any other logging library. However, it
19
+ tries to make your log mesages more readable. By default, it will format the given
20
+ message as follows:
21
+
22
+ ```ruby
23
+ logger = Yell.new STDOUT
24
+
25
+ logger.info "Hello World"
26
+ "2012-02-29T09:30:00+01:00 [ INFO] 65784 : Hello World"
27
+ #^ ^ ^ ^
28
+ #ISO8601 Timestamp Level Pid message
29
+ ```
30
+
31
+ You can alternate the formatting, or completely disable it altogether. See the
32
+ next examples for reference:
33
+
34
+ ```ruby
35
+ # No formatting
36
+ logger = Yell.new STDOUT, :format => false
37
+ logger.info "No formatting"
38
+ "No formatting"
39
+
40
+ # Alternate formatting
41
+ logger = Yell.new STDOUT, :format => "'%m' at %d"
42
+ logger.info "Alternate formatting"
43
+ "'Alternate format' at 2012-02-29T09:30:00+01:00"
44
+ ```
45
+
46
+ As you can see, it basically is just string interpolation with a few reserved
47
+ captures. You can see the list at #Yell::Formatter.
48
+
49
+ When no arguments are given, Yell will check for `ENV['RACK_ENV']` and
50
+ determine the filename from that.
51
+
52
+ Alternatively, you may define `ENV['YELL_ENV']` to set the filename. If neither
53
+ `YELL_ENV` or `RACK_ENV` is defined, `'development'` will be the default. Also, if a
54
+ `log` directory exists, Yell will place the file there (only if you have not passed
55
+ a filename explicitly.
56
+
57
+ Naturally, if you pass a `:filename` to Yell:
58
+
59
+ ```ruby
60
+ logger = Yell.new 'custom.log'
61
+ ```
62
+
63
+ Copyright © 2011-2012 Rudolf Schmidt, released under the MIT license
64
+
data/lib/yell.rb CHANGED
@@ -1,19 +1,58 @@
1
- module Yell
1
+ # encoding: utf-8
2
2
 
3
- autoload :Config, File.dirname(__FILE__) + '/yell/config'
4
- autoload :Formatter, File.dirname(__FILE__) + '/yell/formatter'
5
- autoload :Logger, File.dirname(__FILE__) + '/yell/logger'
6
- autoload :Adapters, File.dirname(__FILE__) + '/yell/adapters'
3
+ # Copyright (c) 2011-2012 Rudolf Schmidt
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7
23
 
8
- class NoSuchAdapter < StandardError; end
24
+ require 'time'
9
25
 
26
+ $: << File.dirname(__FILE__)
10
27
 
11
- def self.new( *args, &block )
28
+ require 'yell/formatter'
29
+ require 'yell/adapters'
30
+ require 'yell/level'
31
+ require 'yell/logger'
32
+
33
+ module Yell #:nodoc:
34
+ # The possible log levels
35
+ Severities = [ 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'UNKNOWN' ]
36
+
37
+ extend self
38
+
39
+ # Creates a new logger instance.
40
+ #
41
+ # Refer to #Yell::Loggger for usage.
42
+ #
43
+ # @returns [Yell::Logger] The logger instance
44
+ def new( *args, &block )
12
45
  Yell::Logger.new( *args, &block )
13
46
  end
14
-
15
- def self.config
16
- @@config ||= Yell::Config.new
47
+
48
+
49
+ def env #:nodoc:
50
+ ENV['YELL_ENV'] || ENV['RACK_ENV'] || 'development'
51
+ end
52
+
53
+ def level( val = nil ) #:nodoc:
54
+ Yell::Level.new( val )
17
55
  end
18
56
 
19
57
  end
58
+
data/lib/yell/adapters.rb CHANGED
@@ -1,21 +1,57 @@
1
- module Yell
1
+ # encoding: utf-8
2
+
3
+ require 'yell/adapters/base'
4
+ require 'yell/adapters/io'
5
+ require 'yell/adapters/file'
6
+ require 'yell/adapters/datefile'
7
+
8
+ module Yell #:nodoc:
9
+
10
+ # NoSuchAdapter is raised whenever you want to instantiate an
11
+ # adapter that does not exist.
12
+ class NoSuchAdapter < StandardError; end
13
+
14
+ # This module provides the interface to attaching adapters to
15
+ # the logger. You should not have to call the corresponding classes
16
+ # directly.
2
17
  module Adapters
18
+ extend self
3
19
 
4
- Dir[ ::File.dirname(__FILE__) + '/adapters/*.rb' ].each do |file|
5
- autoload ::File.basename(file, '.rb').capitalize, file
6
- end
20
+ # Returns an instance of the given processor type.
21
+ #
22
+ # @example A simple file adapter
23
+ # Yell::Adapters[ :file ]
24
+ def []( type, options = {}, &block )
25
+ # return type if type.instance_of?(Yell::Adapters::)
26
+
27
+ if type.instance_of?( ::IO )
28
+ # should apply to STDOUT, STDERR, File, etc
29
+ Yell::Adapters::Io.new( type, options, &block )
30
+ else
31
+ # any other type
32
+ adapter = case type
33
+ when String, Symbol then self.const_get( camelize(type.to_s) )
34
+ else type
35
+ end
7
36
 
8
- # returns an instance of the given processor type
9
- def self.new( type, options = {}, &block )
10
- klass = case type
11
- when String, Symbol then const_get( type.to_s.capitalize )
12
- else type
37
+ adapter.new( options, &block )
13
38
  end
14
-
15
- klass.new( options, &block )
16
- rescue NameError => e
17
- raise Yell::NoSuchAdapter, "no such adapter #{type.inspect}"
18
39
  end
19
-
40
+
41
+
42
+ private
43
+
44
+ # Simple camelcase converter.
45
+ #
46
+ # @example
47
+ # camelize("file")
48
+ # #=> "File"
49
+ #
50
+ # camelize("date_file")
51
+ # #=> "DateFile"
52
+ def camelize( str )
53
+ str.capitalize.gsub( /(_\w)/ ) { |match| match.reverse.chop.upcase }
54
+ end
55
+
20
56
  end
21
57
  end
Binary file
@@ -1,72 +1,55 @@
1
- module Yell::Adapters
2
- class Base
1
+ # encoding: utf-8
3
2
 
4
- attr_reader :name # name of the logger this adapter belongs to
3
+ module Yell #:nodoc:
4
+ module Adapters #:nodoc:
5
5
 
6
- # Initialize a new base adapter. Other adapters should inherit from it.
7
- def initialize ( options = {}, &block )
8
- @block = block
9
-
10
- @name = options[:name] || Yell.config.env
11
- @level, @data = '', '' # default
12
- end
13
-
14
- def call ( level, msg )
15
- reset!(true) if reset? # connect / get a file handle or whatever
16
-
17
- @level, @message = level, msg
18
-
19
- # self.instance_eval &@block if @block
20
- @block.call( self ) if @block
21
- end
22
-
23
- def message
24
- return @message if @message.is_a?( String )
25
-
26
- @message.inspect
27
- end
28
-
29
- # Convenience method for resetting the processor.
6
+ # This class provides the basic interface for all allowed
7
+ # operations on any adapter implementation.
30
8
  #
31
- # @param [true, false] now Perform the reset immediately? (default false)
32
- def reset!( now = false )
33
- close
34
- open if now
35
- end
36
-
37
- # Opens the logfile or connection (in case of a database adapter)
38
- def open
39
- open! unless opened?
40
- end
9
+ # Other adapters should include it for the base methods used
10
+ # by the {Yell::Logger}.
11
+ module Base
12
+
13
+ # The main method for calling the adapter.
14
+ #
15
+ # Adapter classes should provide their own implementation
16
+ # of this method.
17
+ def write( level, message )
18
+ nil
19
+ end
20
+
21
+ # Determine whether to write at the given severity.
22
+ #
23
+ # @example
24
+ # write? :error
25
+ #
26
+ # @param [String,Symbol,Integer] severity The severity to ask if to write or not.
27
+ #
28
+ # @return [Boolean] true or false
29
+ def write?( severity )
30
+ @level.nil? || @level.at?( severity )
31
+ end
32
+
33
+ # Close the adapter (stream, connection, etc).
34
+ #
35
+ # Adapter classes should provide their own implementation
36
+ # of this method.
37
+ def close
38
+ nil
39
+ end
40
+
41
+ # Set the log level.
42
+ #
43
+ # @example Set minimum level to :info
44
+ # level :info
45
+ #
46
+ # For more examples, refer to {Yell::Level}.
47
+ def level( severity = nil )
48
+ @level = Yell::Level.new( severity )
49
+ end
41
50
 
42
- # Closes the file handle or connection (in case of a database adapter)
43
- def close
44
- close! unless closed?
45
- end
46
-
47
-
48
- private
49
-
50
- def reset?; closed?; end
51
-
52
- # stub
53
- def open!
54
- raise "Not implemented"
55
- end
56
-
57
- def opened?; false; end
58
-
59
- # stub
60
- def close!
61
- raise "Not implemented"
62
- end
63
-
64
- def closed?; !opened?; end
65
-
66
- # stub
67
- def write
68
- raise 'Not implemented'
69
51
  end
70
52
 
71
53
  end
72
54
  end
55
+
@@ -1,38 +1,63 @@
1
- module Yell::Adapters
2
- class Datefile < Yell::Adapters::File
1
+ # encoding: utf-8
3
2
 
4
- DefaultDatePattern = "%Y%m%d"
3
+ module Yell #:nodoc:
4
+ module Adapters #:nodoc:
5
5
 
6
- def initialize ( options = {}, &block )
7
- @date_pattern = options[:date_pattern] || DefaultDatePattern
8
- @date = nil # default; do not override --R
6
+ # The +Datefile+ adapter is similar to the +File+ adapter. However, it
7
+ # rotates the file at midnight.
8
+ class Datefile < Yell::Adapters::File
9
9
 
10
- @file_basename = options[:filename] || default_filename
11
- options[:filename] = @file_basename
10
+ # The default date pattern, e.g. "19820114" (14 Jan 1982)
11
+ DefaultDatePattern = "%Y%m%d"
12
12
 
13
- super( options, &block )
14
- end
13
+ def initialize( options = {}, &block )
14
+ @date_pattern = options[:date_pattern] || DefaultDatePattern
15
15
 
16
- def reset!( now )
17
- @filename = new_filename
18
- super( now )
19
- end
16
+ @file_basename = options[:filename] || default_filename
17
+ options[:filename] = @file_basename
20
18
 
19
+ @date = nil # default; do not override --R
20
+
21
+ super( options, &block )
22
+ end
21
23
 
22
- private
24
+ def write( level, message )
25
+ close if close?
23
26
 
24
- def reset?
25
- _date = Time.now.strftime( @date_pattern )
26
- unless opened? && _date == @date
27
- @date = _date
28
- return true
27
+ super( level, message )
29
28
  end
30
29
 
31
- false
32
- end
30
+ # @override Reset the file handle
31
+ def close
32
+ @filename = new_filename
33
+
34
+ super
35
+ end
36
+
37
+
38
+ private
39
+
40
+ # Determines whether to close the file handle or not.
41
+ #
42
+ # It is based on the `:date_pattern` (can be passed as option upon initialize).
43
+ # If the current time hits the pattern, it closes the file stream.
44
+ #
45
+ # @return [Boolean] true or false
46
+ def close?
47
+ _date = Time.now.strftime( @date_pattern )
48
+ if !@stream or _date != @date
49
+ @date = _date
50
+ return true
51
+ end
52
+
53
+ false
54
+ end
55
+
56
+ # Sets the filename with the `:date_pattern` appended to it.
57
+ def new_filename
58
+ @file_basename.sub( /(\.\w+)?$/, ".#{@date}\\1" )
59
+ end
33
60
 
34
- def new_filename
35
- @file_basename.sub( /(\.\w+)?$/, ".#{@date}\\1" )
36
61
  end
37
62
 
38
63
  end
@@ -1,95 +1,32 @@
1
- module Yell::Adapters
2
- class File < Yell::Adapters::Base
1
+ # encoding: utf-8
3
2
 
4
- Colors = {
5
- 'DEBUG' => "\e[32;1m", # green;bold
6
- # 'INFO' => "\e[0m", # white
7
- 'WARN' => "\e[33;1m", # yello;bold
8
- 'ERROR' => "\e[31;1m", # red;bold
9
- 'FATAL' => "\e[35;1m", # magenta;bold
10
- 'UNKNOWN' => "\e[36m", # cyan
11
- 'DEFAULT' => "\e[0m" # NONE
12
- }
3
+ module Yell #:nodoc:
4
+ module Adapters #:nodoc:
13
5
 
14
- def initialize ( options = {}, &block )
15
- super
6
+ # The +File+ adapter is the most basic. As one would expect, it's used
7
+ # for logging into files.
8
+ class File < Yell::Adapters::Io
16
9
 
17
- @formatter = formatter_from( options )
10
+ def initialize( options = {}, &block )
11
+ super( nil, options, &block )
18
12
 
19
- @colorize = options[:colorize]
20
- @filename = options[:filename] || default_filename
21
-
22
- @file_prefix, @file_suffix = options[:file_prefix], options[:file_suffix]
23
-
24
- # validate the filename
25
- unless @filename.is_a?( String )
26
- raise( TypeError, "Argument :filename must be a string." )
13
+ @filename = options.fetch(:filename, default_filename)
27
14
  end
28
- end
29
-
30
- def call ( level, msg )
31
- super # Base call
32
-
33
- msg = @formatter.format( @level, message )
34
- msg = colorize( @level, msg ) if @colorize
35
- msg << "\n" unless msg[-1] == ?\n # add new line if there is none
36
15
 
37
- write( msg )
38
- end
39
-
40
-
41
- private
42
-
43
- def write ( msg )
44
- @file.print( msg )
45
- @file.flush
46
- rescue Exception => e
47
- close # make sure the file gets closed
48
- raise( e ) # re-raise the error
49
- end
50
-
51
- def close!
52
- @file.close
53
- @file = nil
54
- end
55
-
56
- def open!
57
- # create directory if not exists
58
- dirname = ::File.dirname( @filename )
59
- FileUtils.mkdir_p( dirname ) unless ::File.directory?( dirname )
16
+ # @override Lazily open the file handle
17
+ def stream
18
+ @stream ||= ::File.open( @filename, ::File::WRONLY|::File::APPEND|::File::CREAT )
19
+ end
60
20
 
61
- # open file for appending if exists, or create a new
62
- filename = [ dirname, "#{@file_prefix}#{::File.basename(@filename)}#{@file_suffix}" ].join( '/' )
63
- @file = ::File.open( filename, ::File::WRONLY|::File::APPEND|::File::CREAT )
64
- end
65
21
 
66
- def opened?; !@file.nil?; end
22
+ private
67
23
 
68
- # Returns the right formatter from the given options
69
- def formatter_from( options )
70
- if options.key?(:formatter)
71
- # this is done, so we can set :formatter => false to just output the given message
72
- options[:formatter] || Yell::Formatter.new( "%m" )
73
- elsif options[:formatter]
74
- # the options :formatter was given
75
- Yell::Formatter.new(
76
- options[:formatter][:pattern],
77
- options[:formatter][:date_pattern]
78
- )
79
- else
80
- # Standard formatter
81
- Yell::Formatter.new
24
+ def default_filename
25
+ ::File.directory?("log") ? "log/#{Yell.env}.log" : "#{Yell.env}.log"
82
26
  end
83
- end
84
-
85
- def colorize( level, msg )
86
- return msg unless color = Colors[level.upcase]
87
- color + msg + Colors['DEFAULT']
88
- end
89
27
 
90
- def default_filename
91
- "#{Yell.config.env}.log"
92
28
  end
93
29
 
94
30
  end
95
31
  end
32
+
@@ -0,0 +1,89 @@
1
+ module Yell
2
+ module Adapters
3
+
4
+ class Io
5
+ include Yell::Adapters::Base
6
+
7
+ # The possible unix log colors
8
+ Colors = {
9
+ 'DEBUG' => "\e[32;1m", # green;bold
10
+ # 'INFO' => "\e[0m", # white
11
+ 'WARN' => "\e[33;1m", # yello;bold
12
+ 'ERROR' => "\e[31;1m", # red;bold
13
+ 'FATAL' => "\e[35;1m", # magenta;bold
14
+ 'UNKNOWN' => "\e[36m", # cyan
15
+ 'DEFAULT' => "\e[0m" # NONE
16
+ }
17
+
18
+ # Accessor to the io stream
19
+ attr_reader :stream
20
+
21
+
22
+ def initialize( stream, options = {}, &block )
23
+ @stream = stream
24
+ @options = options
25
+
26
+ level options.fetch(:level, nil)
27
+ format options.fetch(:format, nil)
28
+ colorize! options.fetch(:colorize, false)
29
+
30
+ instance_eval &block if block
31
+ end
32
+
33
+ # Main method to calling the file adapter.
34
+ #
35
+ # The method formats the message and writes it to the file handle.
36
+ #
37
+ # @example
38
+ # write( 'info', 'Hello World' )
39
+ def write( level, message )
40
+ msg = @formatter.format( level, message )
41
+
42
+ # colorize if applicable
43
+ if colorize? and color = Colors[level]
44
+ msg = color + msg + Colors['DEFAULT']
45
+ end
46
+
47
+ msg << "\n" unless msg[-1] == ?\n # add new line if there is none
48
+
49
+ write!( msg )
50
+ end
51
+
52
+ # Set the format for your message.
53
+ def format( pattern, date_pattern = nil )
54
+ @formatter = case pattern
55
+ when Yell::Formatter then pattern
56
+ when false then Yell::Formatter.new( "%m" )
57
+ else Yell::Formatter.new( pattern, date_pattern )
58
+ end
59
+ end
60
+
61
+ # Enable colorizing the log output.
62
+ def colorize!( color = true )
63
+ @colorize = color
64
+ end
65
+
66
+ # Determie whether to colorize the log output or nor
67
+ #
68
+ # @return [Boolean] true or false
69
+ def colorize?; !!@colorize; end
70
+
71
+
72
+ private
73
+
74
+ # TODO: Implement Buffer to not flush immediately.
75
+ def write!( message )
76
+ stream.print( message )
77
+ stream.flush
78
+ rescue => e
79
+ close
80
+
81
+ # re-raise the exception
82
+ raise( e, caller )
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+ end
89
+
@@ -1,37 +1,39 @@
1
- module Yell
1
+ # encoding: utf-8
2
+
3
+ module Yell #:nodoc:
4
+
5
+ # The +Formatter+ provides a handle to configure your log message style.
2
6
  class Formatter
3
7
 
4
8
  PatternTable = {
5
- "m" => "message",
9
+ "m" => "message",
6
10
  "d" => "date",
7
- "l" => "level.downcase",
11
+ "l" => "level[0]",
8
12
  "L" => "level.upcase",
9
- "p" => "$$", # pid
13
+ "p" => "$$",
10
14
  "h" => "hostname"
11
15
  }
12
16
  PatternRegexp = /([^%]*)(%\d*)?([dlLphm])?(.*)/
13
-
14
17
  DefaultPattern = "%d [%5L] %p %h: %m"
15
- DefaultDatePattern = "%Y-%m-%d %H:%M:%S" # ISO8601
16
-
17
-
18
- def initialize ( pattern = nil, date_pattern = nil )
19
- @pattern = pattern || DefaultPattern
20
- @date_pattern = date_pattern || DefaultDatePattern
21
-
22
- define_format_method
18
+
19
+
20
+ def initialize( pattern = nil, date_pattern = nil )
21
+ @pattern = pattern || DefaultPattern
22
+ @date_pattern = date_pattern
23
+
24
+ define!
23
25
  end
24
-
26
+
25
27
 
26
28
  private
27
29
 
28
- def define_format_method
30
+ def define!
29
31
  buff, args, _pattern = "", [], @pattern.dup
30
-
32
+
31
33
  while true
32
34
  match = PatternRegexp.match( _pattern )
33
35
 
34
- buff << match[1] unless match[1].empty?
36
+ buff << match[1] unless match[1].empty?
35
37
  break if match[2].nil?
36
38
 
37
39
  buff << match[2] + 's' # '%s'
@@ -41,18 +43,21 @@ module Yell
41
43
  end
42
44
 
43
45
  instance_eval %-
44
- def format ( level, message )
46
+ def format( level, message )
45
47
  sprintf( "#{buff}", #{args.join(',')} )
46
48
  end
47
49
  -
48
50
  end
49
51
 
50
- def date; Time.now.strftime( @date_pattern ); end
51
-
52
+ def date
53
+ @date_pattern ? Time.now.strftime( @date_pattern ) : Time.now.iso8601
54
+ end
55
+
52
56
  def hostname
53
57
  return @hostname if defined?( @hostname )
54
58
  @hostname = Socket.gethostname rescue nil
55
59
  end
56
60
 
57
61
  end
58
- end
62
+ end
63
+
data/lib/yell/level.rb ADDED
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+
3
+ module Yell #:nodoc:
4
+
5
+ # The +Level+ class handles the severities for you in order to determine
6
+ # if an adapter should log or not.
7
+ #
8
+ # In order to setup your level, you have certain modifiers available:
9
+ # at :warn # will be set to :warn level only
10
+ # gt :warn # Will set from :error level onwards
11
+ # gte :warn # Will set from :warn level onwards
12
+ # lt :warn # Will set from :info level an below
13
+ # lte :warn # Will set from :warn level and below
14
+ #
15
+ #
16
+ # You are able to combine those modifiers to your convenience.
17
+ #
18
+ # @example Set from :info to :error (including)
19
+ # Yell::Level.new(:info).lte(:error)
20
+ #
21
+ # @example Set from :info to :error (excluding)
22
+ # Yell::Level.new(:info).lt(:error)
23
+ #
24
+ # @example Set at :info only
25
+ # Yell::Level.new.at(:info)
26
+ class Level
27
+
28
+ # Create a new level instance.
29
+ #
30
+ # @example Enable all severities
31
+ # Yell::Level.new
32
+ #
33
+ # @example Pass the minimum possible severity
34
+ # Yell::Level.new :warn
35
+ #
36
+ # @example Pass an array to exactly set the level at the given severities
37
+ # Yell::Level.new [:info, :error]
38
+ #
39
+ # @example Pass a range to set the level within the severities
40
+ # Yell::Level.new (:info..:error)
41
+ #
42
+ # @param [Integer,String,Symbol,Array,Range,nil] severity The severity for the level.
43
+ def initialize( severity = nil )
44
+ @severities = Yell::Severities.map { true } # all levels allowed by default
45
+
46
+ case severity
47
+ when Array then severity.each { |s| at(s) }
48
+ when Range then gte(severity.first).lte(severity.last)
49
+ when Integer, String, Symbol then gte(severity)
50
+ end
51
+ end
52
+
53
+ # Returns whether the level is allowed at the given severity
54
+ #
55
+ # @example
56
+ # at? :warn
57
+ # at? 0 # debug
58
+ def at?( severity )
59
+ index = index_from( severity )
60
+
61
+ index.nil? ? false : @severities[index]
62
+ end
63
+
64
+ def at( severity ) #:nodoc:
65
+ calculate! :==, severity
66
+ self
67
+ end
68
+
69
+ def gt( severity ) #:nodoc:
70
+ calculate! :>, severity
71
+ self
72
+ end
73
+
74
+ def gte( severity ) #:nodoc:
75
+ calculate! :>=, severity
76
+ self
77
+ end
78
+
79
+ def lt( severity ) #:nodoc:
80
+ calculate! :<, severity
81
+ self
82
+ end
83
+
84
+ def lte( severity ) #:nodoc:
85
+ calculate! :<=, severity
86
+ self
87
+ end
88
+
89
+
90
+ private
91
+
92
+ def calculate!( modifier, severity )
93
+ index = index_from( severity )
94
+ return if index.nil?
95
+
96
+ case modifier
97
+ when :> then ascending!( index+1 )
98
+ when :>= then ascending!( index )
99
+ when :< then descending!( index-1 )
100
+ when :<= then descending!( index )
101
+ else set!( index ) # :==
102
+ end
103
+
104
+ taint unless tainted?
105
+ end
106
+
107
+ def index_from( severity )
108
+ case severity
109
+ when Integer then severity
110
+ when String, Symbol then Yell::Severities.index( severity.to_s.upcase )
111
+ else nil
112
+ end
113
+ end
114
+
115
+ def ascending!( index )
116
+ @severities.each_with_index do |s, i|
117
+ next if s == false # skip
118
+
119
+ @severities[i] = i < index ? false : true
120
+ end
121
+ end
122
+
123
+ def descending!( index )
124
+ @severities.each_with_index do |s, i|
125
+ next if s == false # skip
126
+
127
+ @severities[i] = index < i ? false : true
128
+ end
129
+ end
130
+
131
+ def set!( index )
132
+ @severities.map! { false } unless tainted?
133
+
134
+ @severities[index] = true
135
+ end
136
+
137
+ end
138
+
139
+ end
data/lib/yell/logger.rb CHANGED
@@ -1,82 +1,145 @@
1
- module Yell
2
- class Logger
3
- Levels = [ 'debug', 'info', 'warn', 'error', 'fatal', 'unknown' ]
1
+ # encoding: utf-8
4
2
 
3
+ module Yell #:nodoc:
5
4
 
5
+ # The +Logger+ is your entrypoint. Anything onwards is derived from here.
6
+ class Logger
7
+ # Creates a new Logger instance
8
+ #
9
+ # @example A standard file logger
10
+ # Yell::Logger.new
11
+ # Yell::Logger.new 'development.log'
12
+ #
13
+ # @example A standard datefile logger
14
+ # Yell::Logger.new :datefile
15
+ # Yell::Logger.new :datefile, 'development.log'
16
+ #
17
+ # @example Setting the log level
18
+ # Yell::Logger.new :level => :warn
19
+ #
20
+ # Yell::Logger.new do
21
+ # level :warn
22
+ # end
23
+ #
24
+ # @example Combined settings
25
+ # Yell::Logger.new 'development.log', :level => :warn
26
+ #
27
+ # Yell::Logger.new :datefile, 'development.log' do
28
+ # level :info
29
+ # end
6
30
  def initialize( *args, &block )
7
31
  @adapters = []
8
32
 
9
33
  # extract options
10
34
  @options = args.last.is_a?(Hash) ? args.pop : {}
11
35
 
36
+ # set the log level when given
37
+ # level @options[:level] if @options[:level]
38
+ level @options[:level] # default
39
+
12
40
  # check if filename was given as argument and put it into the @options
13
41
  if args.last.is_a?( String )
14
42
  @options[:filename] = args.pop unless @options[:filename]
15
43
  end
16
44
 
17
- @default_adapter = args.last.is_a?( Symbol ) ? args.pop : :file
45
+ # extract adapter
46
+ adapter args.pop if args.any?
18
47
 
19
- # eval the given block if any
20
- instance_eval( &block ) if block
48
+ # set the log level when given
49
+ level @options[:level] if @options[:level]
21
50
 
22
- build
23
- end
51
+ # eval the given block
52
+ instance_eval &block if block
24
53
 
25
- # === the following methods are used for the logger setup
26
- def adapter ( type, options = {}, &block )
27
- @adapters << Yell::Adapters.new( type, @options.merge(options), &block )
28
- rescue LoadError => e
29
- raise Yell::NoSuchAdapter, e.message
54
+ define!
30
55
  end
31
56
 
32
- def level ( val )
33
- @level = case val
34
- when Integer then val
35
- when String, Symbol then Levels.index( val.to_s )
36
- else nil
57
+ # Define an adapter to be used for logging.
58
+ #
59
+ # @example Standard adapter
60
+ # adapter :file
61
+ #
62
+ # @example Standard adapter with filename
63
+ # adapter :file, 'development.log'
64
+ #
65
+ # # Alternative notation for filename in options
66
+ # adapter :file, :filename => 'developent.log'
67
+ #
68
+ # @example Standard adapter with filename and additional options
69
+ # adapter :file, 'development.log', :level => :warn
70
+ #
71
+ # @example Set the adapter directly from an adapter instance
72
+ # adapter( Yell::Adapter::File.new )
73
+ #
74
+ # @param [Symbol] type The type of the adapter, may be `:file` or `:datefile` (default `:file`)
75
+ #
76
+ # @return A new +Yell::Adapter+ instance
77
+ #
78
+ # @raise [Yell::NoSuchAdapter] Will be thrown when the adapter is not defined
79
+ def adapter( type = :file, *args, &block )
80
+ options = [@options, *args].inject( Hash.new ) do |h,c|
81
+ h.merge( c.is_a?(String) ? {:filename => c} : c )
37
82
  end
83
+
84
+ @adapters << Yell::Adapters[ type, options, &block ]
85
+ rescue NameError => e
86
+ raise Yell::NoSuchAdapter, type
87
+ end
88
+
89
+ # Set the minimum log level.
90
+ #
91
+ # @example Set the level to :warn
92
+ # level :warn
93
+ #
94
+ # @param [String, Symbol, Integer] val The minimum log level
95
+ def level( val = nil )
96
+ @level = Yell::Level.new( val )
38
97
  end
39
98
 
40
99
  # Convenience method for resetting all adapters of the Logger.
41
100
  #
42
- # @param [true, false] now Perform the reset immediately? (default false)
43
- def reset!( now = false )
44
- close
45
- open if now
101
+ # @param [Boolean] now Perform the reset immediately (default false)
102
+ def close( now = false )
103
+ @adapters.each(&:close)
46
104
  end
47
105
 
48
- def close; @adapters.each(&:close); end
49
- def open; @adapters.each(&:open); end
50
106
 
51
-
52
107
  private
53
108
 
54
- def build
55
- adapter @default_adapter if @adapters.empty? # default adapter when none defined
109
+ # Sets a default adapter if none was given explicitly and defines the log methods on
110
+ # the logger instance.
111
+ def define!
112
+ adapter :file if @adapters.empty? # default adapter when none defined
56
113
 
57
- define_log_methods
114
+ define_log_methods!
58
115
  end
59
116
 
60
- def define_log_methods
61
- Levels.each_with_index do |name, index|
62
- instance_eval %-
63
- def #{name}?; #{@level.nil? || index >= @level}; end
64
-
65
- def #{name} ( data = '' )
66
- return unless #{name}?
117
+ # Creates instance methods for every defined log level (debug, info, ...) depending
118
+ # on whether anything should be logged upon, for instance, #info.
119
+ def define_log_methods!
120
+ Yell::Severities.each do |l|
121
+ name = l.downcase
67
122
 
68
- data = yield if block_given?
69
- process( "#{name}", data )
70
-
71
- data
72
- end
123
+ instance_eval %-
124
+ def #{name}?; #{@level.at?(name)}; end # def info?; true; end
125
+ #
126
+ def #{name}( message = nil ) # def info( message = nil )
127
+ return unless #{name}? # return unless info?
128
+ #
129
+ message = yield if block_given? # message = yield if block_given?
130
+ write( "#{l}", message ) # write( "INFO", message )
131
+ #
132
+ true # true
133
+ end # end
73
134
  -
74
135
  end
75
136
  end
76
137
 
77
- def process ( level, data )
78
- @adapters.each { |a| a.call( level, data ) }
138
+ # Cycles all the adapters and writes the message
139
+ def write( level, message )
140
+ @adapters.each { |a| a.write(level, message) if a.write?(level) }
79
141
  end
80
142
 
81
143
  end
82
144
  end
145
+
data/lib/yell/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Yell
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
 
4
4
  end
data/spec/spec_helper.rb CHANGED
@@ -7,14 +7,5 @@ require 'rspec'
7
7
  require 'timecop'
8
8
 
9
9
  RSpec.configure do |config|
10
-
11
- config.before do
12
- Yell.config.stub!( :yaml_file ).and_return( File.dirname(__FILE__) + '/config/yell.yml' )
13
- end
14
-
15
- config.after do
16
- Yell.config.reload! # to not run into caching problems during tests
17
- end
18
-
19
10
  end
20
11
 
data/spec/yell_spec.rb CHANGED
@@ -15,3 +15,4 @@ describe Yell do
15
15
  end
16
16
 
17
17
  end
18
+
data/yell.gemspec CHANGED
@@ -9,10 +9,10 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Rudolf Schmidt"]
10
10
 
11
11
  s.homepage = "http://rubygems.org/gems/yell"
12
- s.summary = %q{Logging library to log into files and databases}
13
- s.description = %q{Logging library to log into files and databases}
12
+ s.summary = %q{Yell - Your Extensible Logging Library }
13
+ s.description = %q{An easy to use logging library to log into files and any other self-defined adapters}
14
14
 
15
- s.rubyforge_project = "d_log"
15
+ s.rubyforge_project = "yell"
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
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: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-23 00:00:00.000000000Z
12
+ date: 2012-03-17 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Logging library to log into files and databases
14
+ description: An easy to use logging library to log into files and any other self-defined
15
+ adapters
15
16
  email:
16
17
  executables: []
17
18
  extensions: []
@@ -19,15 +20,19 @@ extra_rdoc_files: []
19
20
  files:
20
21
  - .gitignore
21
22
  - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
22
25
  - Rakefile
23
26
  - init.rb
24
27
  - lib/yell.rb
25
28
  - lib/yell/adapters.rb
29
+ - lib/yell/adapters/.file.rb.swp
26
30
  - lib/yell/adapters/base.rb
27
31
  - lib/yell/adapters/datefile.rb
28
32
  - lib/yell/adapters/file.rb
29
- - lib/yell/config.rb
33
+ - lib/yell/adapters/io.rb
30
34
  - lib/yell/formatter.rb
35
+ - lib/yell/level.rb
31
36
  - lib/yell/logger.rb
32
37
  - lib/yell/version.rb
33
38
  - spec/spec.opts
@@ -53,9 +58,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
58
  - !ruby/object:Gem::Version
54
59
  version: '0'
55
60
  requirements: []
56
- rubyforge_project: d_log
57
- rubygems_version: 1.8.10
61
+ rubyforge_project: yell
62
+ rubygems_version: 1.8.17
58
63
  signing_key:
59
64
  specification_version: 3
60
- summary: Logging library to log into files and databases
61
- test_files: []
65
+ summary: Yell - Your Extensible Logging Library
66
+ test_files:
67
+ - spec/spec.opts
68
+ - spec/spec_helper.rb
69
+ - spec/yell_spec.rb
data/lib/yell/config.rb DELETED
@@ -1,53 +0,0 @@
1
- module Yell
2
- class Config
3
-
4
- def initialize( yaml_file = nil )
5
- @yaml_file = yaml_file
6
-
7
- reload!
8
- end
9
-
10
- def reload!; @options = nil; end
11
-
12
- def []( key ); options[key]; end
13
-
14
- def options
15
- @options ||= begin
16
- if yaml_file_defined?
17
- require 'yaml'
18
- require 'erb'
19
-
20
- (YAML.load( ERB.new( File.read( yaml_file ) ).result ) || {})[ env ] || {}
21
- else
22
- {} # default
23
- end
24
- end
25
- end
26
-
27
- def env
28
- ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'default'
29
- end
30
-
31
- def root
32
- return ENV['RAILS_ROOT'] if ENV['RAILS_ROOT']
33
- return RAILS_ROOT if defined?( RAILS_ROOT )
34
-
35
- '.'
36
- end
37
-
38
-
39
- private
40
-
41
- def yaml_file_defined?
42
- yaml_file && File.exist?( yaml_file )
43
- end
44
-
45
- # Locates the yell.yml file. The file can end in .yml or .yaml,
46
- # and be located in the current directory (eg. project root) or
47
- # in a .config/ or config/ subdirectory of the current directory.
48
- def yaml_file
49
- @yaml_file || Dir.glob( "#{root}/{,.config/,config/}yell{.yml,.yaml}" ).first
50
- end
51
-
52
- end
53
- end