catamaran 0.2.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/.gitignore +18 -0
- data/README.md +67 -0
- data/catamaran.gemspec +14 -0
- data/init.rb +1 -0
- data/lib/catamaran/formatter/caller_formatter.rb +33 -0
- data/lib/catamaran/formatter/no_caller_formatter.rb +32 -0
- data/lib/catamaran/integration/rails.rb +24 -0
- data/lib/catamaran/log_level.rb +73 -0
- data/lib/catamaran/logger.rb +366 -0
- data/lib/catamaran/manager.rb +145 -0
- data/lib/catamaran/output_file.rb +18 -0
- data/lib/catamaran/outputter.rb +27 -0
- data/lib/catamaran/version.rb +4 -0
- data/lib/catamaran.rb +54 -0
- data/lib/generators/catamaran/install_generator.rb +21 -0
- data/lib/generators/catamaran/templates/catamaran/development.rb +15 -0
- data/lib/generators/catamaran/templates/catamaran/production.rb +2 -0
- data/lib/generators/catamaran/templates/catamaran/staging.rb +2 -0
- data/lib/generators/catamaran/templates/catamaran/test.rb +2 -0
- data/lib/generators/catamaran/templates/catamaran.rb +5 -0
- data/spec/catamaran_spec.rb +191 -0
- data/spec/spec_helper.rb +10 -0
- metadata +77 -0
data/.gitignore
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Catamaran
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
I think logging is a powerful and often undervalued tool in software development. When done right, it's a great way to document code, and it provides a simple & effective way to solve problems when things go awry. All an important part of maintainable code.
|
|
5
|
+
|
|
6
|
+
Gemfile
|
|
7
|
+
-------
|
|
8
|
+
|
|
9
|
+
gem 'catamaran', :git => 'git://github.com/jgithub/catamaran.git'
|
|
10
|
+
|
|
11
|
+
Rails-related setup:
|
|
12
|
+
|
|
13
|
+
rails g catamaran:install
|
|
14
|
+
|
|
15
|
+
Now modify `development.rb` as needed
|
|
16
|
+
|
|
17
|
+
Quickstart coding
|
|
18
|
+
-----------------
|
|
19
|
+
|
|
20
|
+
class WelcomeController < ApplicationController
|
|
21
|
+
LOGGER = CatLogger.MyCompany.MyAppName.App.Controller.WelcomeController
|
|
22
|
+
|
|
23
|
+
def index
|
|
24
|
+
# LOGGER.io methods are reserved for logs related to entering and returning from methods
|
|
25
|
+
LOGGER.io "Entering with params = #{params}" if LOGGER.io?
|
|
26
|
+
|
|
27
|
+
LOGGER.trace "This is my TRACE log" if LOGGER.trace?
|
|
28
|
+
LOGGER.debug "This is my DEBUG log" if LOGGER.debug?
|
|
29
|
+
LOGGER.info "This is my INFO log" if LOGGER.info?
|
|
30
|
+
LOGGER.warn "This is my WARN log" if LOGGER.warn?
|
|
31
|
+
LOGGER.error "This is my ERROR log" if LOGGER.error?
|
|
32
|
+
|
|
33
|
+
LOGGER.io "Returning" if LOGGER.io?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Load the `index` page and check out your `development.log` file
|
|
38
|
+
|
|
39
|
+
Sample log entry (in your development.log file)
|
|
40
|
+
-----------------------------------------------
|
|
41
|
+
IO pid-86000 [2013-12-17 17:26:39:176] MyAppName.App.Controller.WelcomeController - Entering with params = {"controller"=>"welcome", "action"=>"index"} (`/my_rails_project/app/controllers/welcome_controller.rb:7`:in `index`)
|
|
42
|
+
|
|
43
|
+
Inspiration
|
|
44
|
+
-----------
|
|
45
|
+
I'm looking for a logging utility that:
|
|
46
|
+
|
|
47
|
+
* records granular timestamps with each log entry
|
|
48
|
+
* records the process ID with each log entry
|
|
49
|
+
* captures from where each log entry was generated
|
|
50
|
+
* works equally well with classes that do and do *not* extend Rails base classes
|
|
51
|
+
* supports the TRACE log level (or other log level less critical than DEBUG).
|
|
52
|
+
* is capable of capturing logs at different log level thresholds from different parts of the app simultaneously
|
|
53
|
+
* readily works with Rails
|
|
54
|
+
|
|
55
|
+
Ideas around what's next
|
|
56
|
+
------------------------
|
|
57
|
+
|
|
58
|
+
* Make it easier to change the formatting pattern
|
|
59
|
+
* Another iteration of improvements on logger.io
|
|
60
|
+
* Consider capturing log messages beyond stderr, stdout, and local files
|
|
61
|
+
* Ongoing performance considerations
|
|
62
|
+
* Something like `filter_parameter_logging`
|
|
63
|
+
* Optional backtrace support for certain logs
|
|
64
|
+
* Color support
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
data/catamaran.gemspec
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/lib/catamaran/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = "catamaran"
|
|
5
|
+
s.version = Catamaran::VERSION
|
|
6
|
+
s.authors = ["Jeano"]
|
|
7
|
+
s.email = ["catamaran@jeano.net"]
|
|
8
|
+
s.summary = "Catamaran Logger"
|
|
9
|
+
s.description = "A logging utility"
|
|
10
|
+
s.homepage = "http://github.com/jgithub/catamaran"
|
|
11
|
+
s.files = `git ls-files`.split("\n")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
data/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/catamaran'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
module Formatter
|
|
3
|
+
##
|
|
4
|
+
# Construct a properly formatted log message based
|
|
5
|
+
|
|
6
|
+
class CallerFormatter
|
|
7
|
+
def self.construct_formatted_message( log_level, path, msg, opts )
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# If the client has specified a __FILE__ or a __LINE__ in the log opts, make use of them
|
|
11
|
+
|
|
12
|
+
if opts
|
|
13
|
+
if opts[:file] && opts[:line]
|
|
14
|
+
path = path + " (#{opts[:file]}:#{opts[:line]})"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Truncate on the left
|
|
19
|
+
if path.length > 42
|
|
20
|
+
updated_path = path.dup[-42,42]
|
|
21
|
+
else
|
|
22
|
+
updated_path = path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Making use of the caller here is slow.
|
|
26
|
+
# TODO: Abstract out the logger so that it's easy to use different formats
|
|
27
|
+
# Implicit return
|
|
28
|
+
sprintf( "%6s pid-#{Process.pid} [#{Time.now.strftime( "%G-%m-%d %H:%M:%S:%L" )}] %42s - #{msg} (#{caller(3)[0]})", LogLevel.to_s(log_level), updated_path )
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
module Formatter
|
|
3
|
+
##
|
|
4
|
+
# Construct a properly formatted log message based
|
|
5
|
+
|
|
6
|
+
class NoCallerFormatter
|
|
7
|
+
def self.construct_formatted_message( log_level, path, msg, opts )
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# If the client has specified a __FILE__ or a __LINE__ in the log opts, make use of them
|
|
11
|
+
|
|
12
|
+
if opts
|
|
13
|
+
if opts[:file] && opts[:line]
|
|
14
|
+
path = path + " (#{opts[:file]}:#{opts[:line]})"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Truncate on the left
|
|
19
|
+
if path.length > 42
|
|
20
|
+
updated_path = path.dup[-42,42]
|
|
21
|
+
else
|
|
22
|
+
updated_path = path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# TODO: Abstract out the logger so that it's easy to use different formats
|
|
26
|
+
# Implicit return
|
|
27
|
+
sprintf( "%6s pid-#{Process.pid} [#{Time.now.strftime( "%G-%m-%d %H:%M:%S:%L" )}] %42s - #{msg}", LogLevel.to_s(log_level), updated_path )
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Ties Rails to Catamaran
|
|
3
|
+
|
|
4
|
+
module Catamaran
|
|
5
|
+
module Integration
|
|
6
|
+
module Rails3
|
|
7
|
+
if defined?( Rails::Railtie )
|
|
8
|
+
class Railtie < Rails::Railtie
|
|
9
|
+
initializer :load_base_catamaran do
|
|
10
|
+
initializer = Rails.root.join( "config", "initializers", "catamaran.rb" )
|
|
11
|
+
require initializer if File.exist?( initializer )
|
|
12
|
+
|
|
13
|
+
Catamaran::Manager.add_output_file( Catamaran::OutputFile.new( Rails.root.join( "log", "#{Rails.env}.log" ) ) )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
initializer :load_environment_specific_catamaran do
|
|
17
|
+
initializer = Rails.root.join( "config", "initializers", "catamaran", "#{Rails.env}.rb" )
|
|
18
|
+
require initializer if File.exist?( initializer )
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
class LogLevel
|
|
3
|
+
TRACE = 1000
|
|
4
|
+
DEBUG = 2000
|
|
5
|
+
INFO = 3000
|
|
6
|
+
WARN = 4000
|
|
7
|
+
ERROR = 5000
|
|
8
|
+
SEVERE = 6000
|
|
9
|
+
FATAL = 7000
|
|
10
|
+
|
|
11
|
+
# By default, logger.io messages will be captured when the log level is DEBUG (IO_LESS_CRITICAL_THAN_INFO)
|
|
12
|
+
# If that's too verbose, the level can be changed to IO_LESS_CRITICAL_THAN_DEBUG and logger.io messages won't be
|
|
13
|
+
# visible unless the log level is set to TRACE.
|
|
14
|
+
# See development.rb for an example of this
|
|
15
|
+
IO_LESS_CRITICAL_THAN_DEBUG = 1080
|
|
16
|
+
IO_LESS_CRITICAL_THAN_INFO = 2080
|
|
17
|
+
|
|
18
|
+
IO = IO_LESS_CRITICAL_THAN_INFO
|
|
19
|
+
|
|
20
|
+
def self.reset
|
|
21
|
+
@@default_log_level = INFO
|
|
22
|
+
|
|
23
|
+
self.send( :remove_const, 'IO' ) if self.const_defined?( 'IO' )
|
|
24
|
+
self.const_set( 'IO', IO_LESS_CRITICAL_THAN_INFO )
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
self.reset()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def self.to_s( log_level )
|
|
31
|
+
case log_level
|
|
32
|
+
when TRACE
|
|
33
|
+
retval = 'TRACE'
|
|
34
|
+
when DEBUG
|
|
35
|
+
retval = 'DEBUG'
|
|
36
|
+
when INFO
|
|
37
|
+
retval = 'INFO'
|
|
38
|
+
when WARN
|
|
39
|
+
retval = 'WARN'
|
|
40
|
+
when ERROR
|
|
41
|
+
retval = 'ERROR'
|
|
42
|
+
when SEVERE
|
|
43
|
+
retval = 'SEVERE'
|
|
44
|
+
when FATAL
|
|
45
|
+
retval = 'FATAL'
|
|
46
|
+
when IO_LESS_CRITICAL_THAN_INFO
|
|
47
|
+
retval = 'IO'
|
|
48
|
+
when IO_LESS_CRITICAL_THAN_DEBUG
|
|
49
|
+
retval = 'IO'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
retval
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.default_log_level
|
|
56
|
+
@@default_log_level
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.default_log_level=( value )
|
|
60
|
+
Catamaran.debugging( "Catamaran::LogLevel#default_log_level=() - Switching the default log level to #{value}" ) if Catamaran.debugging?
|
|
61
|
+
@@default_log_level = value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.log_level_io=( value )
|
|
65
|
+
self.send( :remove_const, 'IO' ) if self.const_defined?( 'IO' )
|
|
66
|
+
self.const_set( 'IO', value )
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.log_level_io
|
|
70
|
+
IO
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
class Logger
|
|
3
|
+
attr_accessor :name
|
|
4
|
+
attr_accessor :path
|
|
5
|
+
attr_reader :parent
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# The getter associated with retrieving the current log level for this logger.
|
|
9
|
+
# Similar is the smart_log_level getter
|
|
10
|
+
|
|
11
|
+
def log_level( opts = {} )
|
|
12
|
+
if instance_variable_defined?( :@log_level ) && @log_level
|
|
13
|
+
retval = @log_level
|
|
14
|
+
elsif self.parent.nil?
|
|
15
|
+
# No parent means this logger(self) is the root logger. So use the default log level
|
|
16
|
+
retval = Catamaran::LogLevel.default_log_level()
|
|
17
|
+
else
|
|
18
|
+
recursive = opts[:recursive]
|
|
19
|
+
if recursive == true
|
|
20
|
+
|
|
21
|
+
# Remember the log level we found so we don't have to recursively look for it ever time
|
|
22
|
+
if !instance_variable_defined?( :@memoized_log_level ) || @memoized_log_level.nil?
|
|
23
|
+
@memoized_log_level = parent.log_level( opts ) if parent
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
retval = @memoized_log_level
|
|
27
|
+
else
|
|
28
|
+
Catamaran.debugging( "Catamaran::Logger#log_level() - non-recrusive request for log level. And log level is nil. This shouldn't happen too often." ) if Catamaran.debugging?
|
|
29
|
+
retval = nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Implicit return
|
|
34
|
+
retval
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# We usually want a logger to have a level. The smart_log_level() reader determines a log level even if one isn't
|
|
39
|
+
# explicitly set on this logger instance by making use of recursion up to the root logger if needed
|
|
40
|
+
|
|
41
|
+
def smart_log_level
|
|
42
|
+
self.log_level( { :recursive => true } )
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Setter used to explicitly set the log level for this logger
|
|
47
|
+
|
|
48
|
+
def log_level=( value )
|
|
49
|
+
@log_level = value
|
|
50
|
+
remove_instance_variable( :@memoized_log_level ) if instance_variable_defined?( :@memoized_log_level )
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Is trace-level logging currently enabled?
|
|
55
|
+
|
|
56
|
+
def trace?
|
|
57
|
+
if self.smart_log_level() <= LogLevel::TRACE
|
|
58
|
+
true
|
|
59
|
+
else
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def trace( msg, opts = nil )
|
|
65
|
+
if trace?
|
|
66
|
+
_write_to_log( LogLevel::TRACE, msg, opts )
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# Is debug-level logging currently enabled?
|
|
72
|
+
|
|
73
|
+
def debug?
|
|
74
|
+
if self.smart_log_level() <= LogLevel::DEBUG
|
|
75
|
+
true
|
|
76
|
+
else
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def debug( msg, opts = nil )
|
|
82
|
+
if debug?
|
|
83
|
+
_write_to_log( LogLevel::DEBUG, msg, opts )
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Is io-level logging currently enabled?
|
|
90
|
+
|
|
91
|
+
def io?
|
|
92
|
+
if self.smart_log_level() <= LogLevel::IO
|
|
93
|
+
true
|
|
94
|
+
else
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def io( msg, opts = nil )
|
|
100
|
+
if io?
|
|
101
|
+
_write_to_log( LogLevel::IO, msg, opts )
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Is info-level logging currently enabled?
|
|
107
|
+
|
|
108
|
+
def info?
|
|
109
|
+
if self.smart_log_level() <= LogLevel::INFO
|
|
110
|
+
true
|
|
111
|
+
else
|
|
112
|
+
false
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def info( msg, opts = nil )
|
|
117
|
+
if info?
|
|
118
|
+
_write_to_log( LogLevel::INFO, msg, opts )
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# Is warn-level logging currently enabled?
|
|
124
|
+
|
|
125
|
+
def warn?
|
|
126
|
+
if self.smart_log_level() <= LogLevel::WARN
|
|
127
|
+
true
|
|
128
|
+
else
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def warn( msg, opts = nil )
|
|
134
|
+
if warn?
|
|
135
|
+
_write_to_log( LogLevel::WARN, msg, opts )
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
# Is error-level logging currently enabled?
|
|
141
|
+
|
|
142
|
+
def error?
|
|
143
|
+
if self.smart_log_level() <= LogLevel::ERROR
|
|
144
|
+
true
|
|
145
|
+
else
|
|
146
|
+
false
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def error( msg, opts = nil )
|
|
151
|
+
if error?
|
|
152
|
+
_write_to_log( LogLevel::ERROR, msg, opts )
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# Is severe-level logging currently enabled?
|
|
158
|
+
|
|
159
|
+
def severe?
|
|
160
|
+
if self.smart_log_level() <= LogLevel::SEVERE
|
|
161
|
+
true
|
|
162
|
+
else
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def severe( msg, opts = nil )
|
|
168
|
+
if severe?
|
|
169
|
+
_write_to_log( LogLevel::SEVERE, msg, opts )
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Is fatal-level logging currently enabled?
|
|
175
|
+
|
|
176
|
+
# def fatal?
|
|
177
|
+
# if self.smart_log_level() <= LogLevel::FATAL
|
|
178
|
+
# true
|
|
179
|
+
# else
|
|
180
|
+
# false
|
|
181
|
+
# end
|
|
182
|
+
# end
|
|
183
|
+
|
|
184
|
+
# def fatal( msg, opts = nil )
|
|
185
|
+
# if fatal?
|
|
186
|
+
# _write_to_log( LogLevel::FATAL, msg, opts )
|
|
187
|
+
# end
|
|
188
|
+
# end
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
# Usually get_logger is a reference to self, unless a path has been specified as a parameter
|
|
192
|
+
|
|
193
|
+
def get_logger( path = nil )
|
|
194
|
+
current_logger = self
|
|
195
|
+
if path
|
|
196
|
+
method_names = path.split(/\./)
|
|
197
|
+
|
|
198
|
+
method_names.each do |method_name|
|
|
199
|
+
current_logger = current_logger.send( method_name.to_sym )
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Implicit return
|
|
204
|
+
current_logger
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# method_missing provides a mechanism to create sub loggers.
|
|
210
|
+
# Let's say I have a logger and I want to create a sub logger
|
|
211
|
+
#
|
|
212
|
+
# root_logger = CatLogger
|
|
213
|
+
# logger_reserved_for_app_stuff = CatLogger.App # App is a missing method
|
|
214
|
+
# logger_reserved_just_for_controller_stuff = CatLogger.App.Controller # Controller is a missing method
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def method_missing( meth, *args, &block )
|
|
218
|
+
Catamaran.debugging( "Catamaran::Logger#method_missing() - meth = #{meth}" )
|
|
219
|
+
|
|
220
|
+
if ( meth.to_sym == :define_method || meth.to_sym == :to_ary )
|
|
221
|
+
# Catamaran.debugging( "Catamaran::Logger#method_missing() - ERROR! Ignoring method: #{meth}" )
|
|
222
|
+
_msg = "[CATAMARAN INTERNAL] Catamaran::Logger#method_missing() - ERROR! Ignoring method: #{meth}"
|
|
223
|
+
$stderr.puts( _msg )
|
|
224
|
+
raise Exception.new _msg
|
|
225
|
+
else
|
|
226
|
+
# Create a new logger object
|
|
227
|
+
logger_obj = Logger.new( meth.to_s, self.path, self )
|
|
228
|
+
|
|
229
|
+
# Keep track of this new sub-logger
|
|
230
|
+
@sub_loggers[meth.to_sym] = logger_obj
|
|
231
|
+
|
|
232
|
+
# Define a method so method_missing will not be called next time this method name is called
|
|
233
|
+
singleton = class << self; self end
|
|
234
|
+
singleton.send :define_method, meth.to_sym, lambda {
|
|
235
|
+
@sub_loggers[meth.to_sym]
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Implicit return
|
|
240
|
+
logger_obj
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
##
|
|
244
|
+
# Human-readable path
|
|
245
|
+
|
|
246
|
+
def path_to_s
|
|
247
|
+
_path = self.path
|
|
248
|
+
if _path
|
|
249
|
+
# Implicit return
|
|
250
|
+
_path.join( delimiter )
|
|
251
|
+
else
|
|
252
|
+
# Implicit return
|
|
253
|
+
nil
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
##
|
|
258
|
+
# Forget any cached memoization log levels within this logger or within sub-loggers of this logger
|
|
259
|
+
|
|
260
|
+
def forget_memoizations
|
|
261
|
+
remove_instance_variable(:@memoized_log_level) if instance_variable_defined?( :@memoized_log_level )
|
|
262
|
+
@sub_loggers.values.each do |logger|
|
|
263
|
+
logger.forget_memoizations()
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
##
|
|
269
|
+
# Number of loggers known about by this logger (including sub-loggers)
|
|
270
|
+
|
|
271
|
+
def num_loggers
|
|
272
|
+
retval = 1 # me!
|
|
273
|
+
@sub_loggers.values.each do |logger|
|
|
274
|
+
retval = retval + logger.num_loggers()
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Implicit return
|
|
278
|
+
Catamaran.debugging( "Catamaran::Logger#num_loggers() - Returning #{retval} from '#{path_to_s()}'" ) if Catamaran.debugging?
|
|
279
|
+
|
|
280
|
+
retval
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def reset
|
|
284
|
+
_reset()
|
|
285
|
+
Catamaran.debugging( "Catamaran::Logger#reset() - Reset complete." ) if Catamaran.debugging?
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def depth
|
|
289
|
+
@depth
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def to_s
|
|
293
|
+
# Implicit return
|
|
294
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}>[name=#{self.name},path=#{path_to_s},depth=#{self.depth},log_level=#{@log_level}]"
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
private
|
|
298
|
+
|
|
299
|
+
##
|
|
300
|
+
# I used to delete the root level logger, but now I reset it instead.
|
|
301
|
+
# Among other reasons, the CatLogger constant now works
|
|
302
|
+
|
|
303
|
+
def _reset
|
|
304
|
+
remove_instance_variable(:@memoized_log_level) if instance_variable_defined?( :@memoized_log_level )
|
|
305
|
+
remove_instance_variable(:@log_level) if instance_variable_defined?( :@log_level )
|
|
306
|
+
|
|
307
|
+
self.name = @initialized_name
|
|
308
|
+
self.path = @initialized_path_so_far ? @initialized_path_so_far.dup : []
|
|
309
|
+
|
|
310
|
+
if @initialized_name && @initialized_name.length > 0 # Root logger has no name
|
|
311
|
+
self.path << @initialized_name
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
@parent = @initialized_parent
|
|
315
|
+
|
|
316
|
+
# @sub_loggers already exist, iterate through each and undefine the associated method missing
|
|
317
|
+
if @sub_loggers
|
|
318
|
+
@sub_loggers.keys.each do |method_name_as_symbol|
|
|
319
|
+
Catamaran.debugging( "Catamaran::Logger#_reset() - Attempting to remove dynamically created method: #{method_name_as_symbol}" ) if Catamaran.debugging?
|
|
320
|
+
|
|
321
|
+
singleton = class << self; self end
|
|
322
|
+
singleton.send :remove_method, method_name_as_symbol
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
@sub_loggers = {}
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
##
|
|
330
|
+
# All log statements eventually call _write_to_log
|
|
331
|
+
|
|
332
|
+
def _write_to_log( log_level, msg, opts )
|
|
333
|
+
formatted_msg = Manager.formatter_class.construct_formatted_message( log_level, self.path_to_s(), msg, opts )
|
|
334
|
+
Outputter.write( formatted_msg )
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
##
|
|
338
|
+
# Initialize this logger.
|
|
339
|
+
|
|
340
|
+
def initialize( name, path_so_far, parent = nil )
|
|
341
|
+
Catamaran.debugging( "Catamaran::Logger#initialize() - Entering with name = #{name ? name : '<nil>'}, path_so_far = #{path_so_far}, parent = #{parent ? parent : '<nil>'}" ) if Catamaran.debugging?
|
|
342
|
+
|
|
343
|
+
@initialized_name = name
|
|
344
|
+
@initialized_path_so_far = path_so_far ? path_so_far.dup : []
|
|
345
|
+
@initialized_parent = parent
|
|
346
|
+
|
|
347
|
+
if parent
|
|
348
|
+
@depth = parent.depth + 1
|
|
349
|
+
else
|
|
350
|
+
@depth = 0
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
_reset()
|
|
354
|
+
|
|
355
|
+
Catamaran.debugging( "Catamaran::Logger#initialize() - I am #{self.to_s}" ) if Catamaran.debugging?
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
##
|
|
359
|
+
# The delimiter is a period
|
|
360
|
+
|
|
361
|
+
def delimiter
|
|
362
|
+
"."
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
end
|
|
366
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
class Manager
|
|
3
|
+
class << self;
|
|
4
|
+
attr_accessor :_root_logger_instance;
|
|
5
|
+
attr_accessor :_stdout_flag;
|
|
6
|
+
attr_accessor :_stderr_flag;
|
|
7
|
+
attr_accessor :_output_files;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Class that's going to be performing the message formatting
|
|
12
|
+
|
|
13
|
+
def formatter_class
|
|
14
|
+
# The NoCallerFormatter class is the default formatter.
|
|
15
|
+
@formatter_class || Catamaran::Formatter::NoCallerFormatter
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def formatter_class=( value )
|
|
19
|
+
@formatter_class = value
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Used to reset Catamaran
|
|
26
|
+
|
|
27
|
+
def self.reset
|
|
28
|
+
Catamaran.debugging( "Catamaran::Manager#reset - Resetting Catamaran" ) if Catamaran.debugging?
|
|
29
|
+
|
|
30
|
+
# Old way. I used to null-out the old logger. But resetting it is better for using the CatLogger constant
|
|
31
|
+
# self.send( :_root_logger_instance=, nil )
|
|
32
|
+
|
|
33
|
+
# New way
|
|
34
|
+
root_logger = self.send( :_root_logger_instance )
|
|
35
|
+
root_logger.reset if root_logger
|
|
36
|
+
|
|
37
|
+
# Also reset the default log level
|
|
38
|
+
Catamaran::LogLevel::reset
|
|
39
|
+
|
|
40
|
+
# Resetting Catamaran probably should not reset the output settings
|
|
41
|
+
# self.send( :_stdout_flag=, nil )
|
|
42
|
+
# self.send( :_stderr_flag=, nil )
|
|
43
|
+
# self.send( :_output_files=, nil )
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Allow the client to access the root logger
|
|
48
|
+
|
|
49
|
+
def self.root_logger
|
|
50
|
+
logger = self.send( :_root_logger_instance )
|
|
51
|
+
|
|
52
|
+
unless logger
|
|
53
|
+
Catamaran.debugging( "Catamaran::Logger#root_logger() - Initializing new root logger" ) if Catamaran.debugging?
|
|
54
|
+
|
|
55
|
+
path_so_far = []
|
|
56
|
+
logger = Logger.new( nil, path_so_far )
|
|
57
|
+
|
|
58
|
+
self.send( :_root_logger_instance=, logger )
|
|
59
|
+
|
|
60
|
+
# Get the instance back as a double-confirm
|
|
61
|
+
logger = self.send( :_root_logger_instance )
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if logger && logger.instance_of?( Catamaran::Logger )
|
|
65
|
+
Catamaran.debugging( "Catamaran::Logger#root_logger() - Returning #{logger}" ) if Catamaran.debugging?
|
|
66
|
+
else
|
|
67
|
+
Catamaran.debugging( "Catamaran::Logger#root_logger() - Error! root logger is not an instance of Logger as would expect" ) if Catamaran.debugging?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
logger
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Specify a destination location for logs
|
|
75
|
+
|
|
76
|
+
def self.add_output_file( output_file )
|
|
77
|
+
unless self.send( :_output_files )
|
|
78
|
+
self.send( :_output_files=, [] )
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
self.send( :_output_files ).push( output_file )
|
|
82
|
+
|
|
83
|
+
Catamaran.debugging( "Catamaran::Logger#add_output_file() - Added output file: #{output_file}. Number of _loggers is now #{self.send( :_output_files ).length}" ) if Catamaran.debugging?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.output_files
|
|
87
|
+
self.send( :_output_files )
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Memoizations are used to cache log levels. Used to clear out the memoization cache.
|
|
92
|
+
|
|
93
|
+
def self.forget_memoizations
|
|
94
|
+
self.root_logger().forget_memoizations()
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# How many loggers is Catamaran currently aware of
|
|
99
|
+
|
|
100
|
+
def self.num_loggers
|
|
101
|
+
self.root_logger().num_loggers()
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
##
|
|
105
|
+
# Call this method with true if you'd like Catamaran to write logs to STDOUT
|
|
106
|
+
|
|
107
|
+
def self.stdout=( bool )
|
|
108
|
+
self.send( :_stdout_flag=, bool )
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
# Is logging to STDOUT enabled?
|
|
113
|
+
|
|
114
|
+
def self.stdout?
|
|
115
|
+
if self.send( :_stdout_flag ) == true
|
|
116
|
+
true
|
|
117
|
+
else
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# Call this method with true if you'd like Catamaran to write logs to STDERR
|
|
124
|
+
|
|
125
|
+
def self.stderr=( bool )
|
|
126
|
+
self.send( :_stderr_flag=, bool )
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# Is logging to STDERR enabled?
|
|
131
|
+
|
|
132
|
+
def self.stderr?
|
|
133
|
+
if self.send( :_stderr_flag ) == true
|
|
134
|
+
true
|
|
135
|
+
else
|
|
136
|
+
false
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private_class_method :_root_logger_instance
|
|
141
|
+
private_class_method :_stdout_flag
|
|
142
|
+
private_class_method :_stderr_flag
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
class OutputFile
|
|
3
|
+
attr_accessor :filename
|
|
4
|
+
|
|
5
|
+
def initialize( filename = nil )
|
|
6
|
+
self.filename = filename
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def write( line )
|
|
10
|
+
unless @file_descriptor
|
|
11
|
+
@file_descriptor = File.open( self.filename, "a" )
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@file_descriptor.puts( line )
|
|
15
|
+
@file_descriptor.flush
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
class Outputter
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
# Write the log message to the appropriate location. Currently that could mean stdout, stderr, or output files.
|
|
6
|
+
|
|
7
|
+
def self.write( formatted_msg )
|
|
8
|
+
if Manager.stdout?
|
|
9
|
+
$stdout.puts( formatted_msg )
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if Manager.stderr?
|
|
13
|
+
$stderr.puts( formatted_msg )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
_output_files = Manager._output_files
|
|
17
|
+
_output_files.each do |output_file|
|
|
18
|
+
begin
|
|
19
|
+
output_file.write( formatted_msg )
|
|
20
|
+
rescue Exception => e
|
|
21
|
+
$stderr.puts( "Catamaran is unable to write to logfile: #{output_file}" )
|
|
22
|
+
end
|
|
23
|
+
end if _output_files
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/catamaran.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'catamaran/logger'
|
|
2
|
+
require 'catamaran/manager'
|
|
3
|
+
require 'catamaran/formatter/caller_formatter'
|
|
4
|
+
require 'catamaran/formatter/no_caller_formatter'
|
|
5
|
+
require 'catamaran/outputter'
|
|
6
|
+
require 'catamaran/output_file'
|
|
7
|
+
|
|
8
|
+
require 'catamaran/log_level'
|
|
9
|
+
require 'catamaran/version'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module Catamaran
|
|
13
|
+
def self.debugging?
|
|
14
|
+
@debugging || false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.debugging( msg )
|
|
18
|
+
$stderr.puts( "[CATAMARAN DEBUGGING] #{msg}" ) if self.debugging?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.debugging=( value )
|
|
22
|
+
@debugging = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.logger( optional_string = nil )
|
|
26
|
+
Catamaran::Manager.root_logger().get_logger( optional_string )
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# get_logger() is an alias for logger()
|
|
31
|
+
class << self
|
|
32
|
+
alias_method :get_logger, :logger
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
LOG_LEVEL_TRACE = LogLevel::TRACE
|
|
36
|
+
LOG_LEVEL_DEBUG = LogLevel::DEBUG
|
|
37
|
+
LOG_LEVEL_INFO = LogLevel::INFO
|
|
38
|
+
LOG_LEVEL_WARN = LogLevel::WARN
|
|
39
|
+
LOG_LEVEL_ERROR = LogLevel::ERROR
|
|
40
|
+
LOG_LEVEL_SEVERE = LogLevel::SEVERE
|
|
41
|
+
LOG_LEVEL_FATAL = LogLevel::FATAL
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# add rails integration
|
|
45
|
+
require('catamaran/integration/rails') if defined?(Rails)
|
|
46
|
+
|
|
47
|
+
# Catamaran::Manager.stderr = true
|
|
48
|
+
# Catamaran::Manager.stdout = false
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Define the CatLogger alias for the Catamaran root logger
|
|
52
|
+
Kernel.send( :remove_const, 'CatLogger' ) if Kernel.const_defined?( 'CatLogger' )
|
|
53
|
+
Kernel.const_set( 'CatLogger', Catamaran.logger )
|
|
54
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Catamaran
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
4
|
+
desc "Generates a custom Catamaran initializer file."
|
|
5
|
+
|
|
6
|
+
def self.source_root
|
|
7
|
+
@_rails_config_source_root ||= File.expand_path("../templates", __FILE__
|
|
8
|
+
)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def copy_initializer
|
|
12
|
+
template "catamaran.rb", "config/initializers/catamaran.rb"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def copy_settings
|
|
16
|
+
directory "catamaran", "config/initializers/catamaran"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Custom settings for Catamaran in development
|
|
3
|
+
|
|
4
|
+
# Enable logging to STDERR
|
|
5
|
+
Catamaran::Manager.stderr = true
|
|
6
|
+
|
|
7
|
+
# The default log level is INFO. Switch to DEBUG in development
|
|
8
|
+
Catamaran::LogLevel.default_log_level = Catamaran::LogLevel::DEBUG
|
|
9
|
+
|
|
10
|
+
# By default, logger.io messages will be captured when the log level is DEBUG (IO_LESS_CRITICAL_THAN_INFO)
|
|
11
|
+
# If that's too verbose, uncomment this
|
|
12
|
+
# Catamaran::LogLevel.log_level_io = Catamaran::LogLevel::IO_LESS_CRITICAL_THAN_DEBUG
|
|
13
|
+
|
|
14
|
+
# The NoCallerFormatter is the default.
|
|
15
|
+
Catamaran::Manager.formatter_class = Catamaran::Formatter::CallerFormatter
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Catamaran do
|
|
4
|
+
it "should define TRACE" do
|
|
5
|
+
Catamaran::LogLevel::TRACE.should > 0
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should have a version" do
|
|
9
|
+
Catamaran::VERSION.length.should > 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should reuse the same root logger instance" do
|
|
13
|
+
Catamaran.logger.object_id.should == Catamaran.logger.object_id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should reuse the same logger instance when contextually the same" do
|
|
17
|
+
Catamaran.logger.Company.Product.App.Model.User.object_id.should == Catamaran.logger.Company.Product.App.Model.User.object_id
|
|
18
|
+
Catamaran.logger( "Company.Product.App.Model.User" ).object_id.should == Catamaran.logger.Company.Product.App.Model.User.object_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should provide access to the root logger through CatLogger" do
|
|
22
|
+
CatLogger.object_id.should == Catamaran::Manager.root_logger.object_id
|
|
23
|
+
Catamaran.logger.object_id.should == CatLogger.object_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should define Catamaran.logger and Catamaran.get_logger methods. They should be equivalent." do
|
|
27
|
+
Catamaran.logger.object_id.should == Catamaran::get_logger.object_id
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should provide access to loggers after a reset" do
|
|
31
|
+
logger = Catamaran.logger.Company.Product.App.Model.User
|
|
32
|
+
Catamaran::Manager.reset
|
|
33
|
+
logger = Catamaran.logger.Company.Product.App.Model.User
|
|
34
|
+
logger.should be_instance_of( Catamaran::Logger )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should create a new loggers for each point in the path" do
|
|
38
|
+
Catamaran.logger
|
|
39
|
+
Catamaran::Manager.num_loggers.should == 1
|
|
40
|
+
Catamaran.logger.Company.Product.App.Model.User
|
|
41
|
+
Catamaran::Manager.num_loggers.should == 6
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should be possible for the user to modify the default log level" do
|
|
45
|
+
Catamaran::LogLevel.default_log_level = Catamaran::LogLevel::TRACE
|
|
46
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::TRACE
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should not provide the same object ID for different paths" do
|
|
50
|
+
Catamaran.logger.A.C.object_id.should_not == Catamaran.logger.B.C
|
|
51
|
+
Catamaran.logger.A.C.object_id.should_not == Catamaran.logger.A.B
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "by default" do
|
|
55
|
+
it "should log INFO messages" do
|
|
56
|
+
logger = Catamaran.logger.Company.Product.App.Model.User
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should NOT log DEBUG messages" do
|
|
60
|
+
logger = Catamaran.logger.Company.Product.App.Model.User
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should have an INFO log level set for the root logger" do
|
|
64
|
+
Catamaran.logger.log_level.should == Catamaran::LogLevel::INFO
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should have an UNSET log level for any non-root loggers" do
|
|
68
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::ERROR
|
|
69
|
+
Catamaran.logger.Test.log_level.should be_nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should have one logger configured" do
|
|
73
|
+
Catamaran::Manager.num_loggers.should == 1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should be that the IO log level is the same as IO_LESS_CRITICAL_THAN_INFO" do
|
|
77
|
+
Catamaran::LogLevel::IO.should == Catamaran::LogLevel::IO_LESS_CRITICAL_THAN_INFO
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context "when the log level is set to INFO" do
|
|
82
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::INFO
|
|
83
|
+
Catamaran.logger.Company.Product.App.Model.User
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "after a reset" do
|
|
87
|
+
it "should have a default log level of INFO" do
|
|
88
|
+
logger = Catamaran.logger
|
|
89
|
+
logger.log_level.should == Catamaran::LogLevel::INFO
|
|
90
|
+
logger.log_level = Catamaran::LogLevel::ERROR
|
|
91
|
+
logger.log_level.should == Catamaran::LogLevel::ERROR
|
|
92
|
+
Catamaran::Manager.reset
|
|
93
|
+
logger.log_level.should == Catamaran::LogLevel::INFO
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should return the number of loggers to 1" do
|
|
97
|
+
Catamaran.logger
|
|
98
|
+
Catamaran::Manager.num_loggers.should == 1
|
|
99
|
+
Catamaran.logger.Company.Product.App.Model.User
|
|
100
|
+
Catamaran::Manager.num_loggers.should == 6
|
|
101
|
+
Catamaran::Manager.reset
|
|
102
|
+
Catamaran::Manager.num_loggers.should == 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should have the same root logger object before and after the reset" do
|
|
106
|
+
before_logger = Catamaran.logger
|
|
107
|
+
Catamaran::Manager.reset
|
|
108
|
+
Catamaran.logger.object_id.should == before_logger.object_id
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe Catamaran.logger do
|
|
113
|
+
it "should be able to inherit it's parent's log level" do
|
|
114
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::ERROR
|
|
115
|
+
Catamaran.logger.Test.smart_log_level.should == Catamaran::LogLevel::ERROR
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe Catamaran::Manager do
|
|
120
|
+
describe "#reset" do
|
|
121
|
+
it "should reset Catamaran" do
|
|
122
|
+
before_reset_logger = Catamaran.logger.Company.Product.App.Model.User
|
|
123
|
+
Catamaran.logger.num_loggers.should == 6
|
|
124
|
+
Catamaran::Manager.reset
|
|
125
|
+
Catamaran.logger.num_loggers.should == 1
|
|
126
|
+
after_reset_logger = Catamaran.logger.Company.Product.App.Model.User
|
|
127
|
+
before_reset_logger.object_id.should_not == after_reset_logger.object_id
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "#forget_memoizations" do
|
|
132
|
+
it "should forget all the memoized log levels" do
|
|
133
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::ERROR
|
|
134
|
+
Catamaran.logger.Test.smart_log_level.should == Catamaran::LogLevel::ERROR
|
|
135
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::INFO
|
|
136
|
+
Catamaran.logger.Test.smart_log_level.should == Catamaran::LogLevel::ERROR
|
|
137
|
+
Catamaran.logger.forget_memoizations
|
|
138
|
+
Catamaran.logger.Test.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe Catamaran::Logger do
|
|
144
|
+
it "should inherit the log level from the root logger as needed" do
|
|
145
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
146
|
+
Catamaran.logger.Company.Product.App.Model.User.log_level.should be_nil
|
|
147
|
+
Catamaran.logger.Company.Product.App.Model.User.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should have a nil log_level unless explicitly set" do
|
|
151
|
+
Catamaran.logger.Company.Product.App.Model.User.log_level.should be_nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "should always have a smart_log_level set" do
|
|
155
|
+
Catamaran.logger.Company.Product.App.Model.User.log_level.should be_nil
|
|
156
|
+
Catamaran.logger.Company.Product.App.Model.User.smart_log_level.should_not be_nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "should write the log if the log has sufficient weight" do
|
|
160
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
161
|
+
Catamaran.logger.should_receive( :_write_to_log ).once
|
|
162
|
+
Catamaran.logger.info( "Testing an INFO log" )
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should NOT write the log if the log does NOT have sufficient" do
|
|
166
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
167
|
+
# DEBUG is disabled
|
|
168
|
+
Catamaran.logger.should_not_receive( :_write_to_log )
|
|
169
|
+
Catamaran.logger.debug( "Testing a DEBUG log" )
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
context "when using smart_log_level" do
|
|
173
|
+
it "should inherit the log level from a parent" do
|
|
174
|
+
Catamaran::LogLevel.default_log_level = Catamaran::LogLevel::INFO
|
|
175
|
+
Catamaran.logger.Company.Product.App.Model.log_level = Catamaran::LogLevel::ERROR
|
|
176
|
+
Catamaran.logger.Company.Product.App.Controller.log_level = Catamaran::LogLevel::WARN
|
|
177
|
+
Catamaran.logger.Company.Product.App.Model.User.smart_log_level.should == Catamaran::LogLevel::ERROR
|
|
178
|
+
Catamaran.logger.Company.Product.App.Controller.UsersController.smart_log_level.should == Catamaran::LogLevel::WARN
|
|
179
|
+
Catamaran.logger.Company.Product.App.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
context "when the log level is specified, the default is no longer used" do
|
|
184
|
+
it "should be " do
|
|
185
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::INFO
|
|
186
|
+
Catamaran.logger.log_level = Catamaran::LogLevel::ERROR
|
|
187
|
+
Catamaran.logger.smart_log_level.should == Catamaran::LogLevel::ERROR
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: catamaran
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jeano
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2013-12-20 00:00:00 -06:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: A logging utility
|
|
17
|
+
email:
|
|
18
|
+
- catamaran@jeano.net
|
|
19
|
+
executables: []
|
|
20
|
+
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
|
|
25
|
+
files:
|
|
26
|
+
- .gitignore
|
|
27
|
+
- README.md
|
|
28
|
+
- catamaran.gemspec
|
|
29
|
+
- init.rb
|
|
30
|
+
- lib/catamaran.rb
|
|
31
|
+
- lib/catamaran/formatter/caller_formatter.rb
|
|
32
|
+
- lib/catamaran/formatter/no_caller_formatter.rb
|
|
33
|
+
- lib/catamaran/integration/rails.rb
|
|
34
|
+
- lib/catamaran/log_level.rb
|
|
35
|
+
- lib/catamaran/logger.rb
|
|
36
|
+
- lib/catamaran/manager.rb
|
|
37
|
+
- lib/catamaran/output_file.rb
|
|
38
|
+
- lib/catamaran/outputter.rb
|
|
39
|
+
- lib/catamaran/version.rb
|
|
40
|
+
- lib/generators/catamaran/install_generator.rb
|
|
41
|
+
- lib/generators/catamaran/templates/catamaran.rb
|
|
42
|
+
- lib/generators/catamaran/templates/catamaran/development.rb
|
|
43
|
+
- lib/generators/catamaran/templates/catamaran/production.rb
|
|
44
|
+
- lib/generators/catamaran/templates/catamaran/staging.rb
|
|
45
|
+
- lib/generators/catamaran/templates/catamaran/test.rb
|
|
46
|
+
- spec/catamaran_spec.rb
|
|
47
|
+
- spec/spec_helper.rb
|
|
48
|
+
has_rdoc: true
|
|
49
|
+
homepage: http://github.com/jgithub/catamaran
|
|
50
|
+
licenses: []
|
|
51
|
+
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: "0"
|
|
62
|
+
version:
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: "0"
|
|
68
|
+
version:
|
|
69
|
+
requirements: []
|
|
70
|
+
|
|
71
|
+
rubyforge_project:
|
|
72
|
+
rubygems_version: 1.3.5
|
|
73
|
+
signing_key:
|
|
74
|
+
specification_version: 3
|
|
75
|
+
summary: Catamaran Logger
|
|
76
|
+
test_files: []
|
|
77
|
+
|