semantic_logger 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ lib = File.expand_path('../lib/', __FILE__)
2
2
  $:.unshift lib unless $:.include?(lib)
3
3
 
4
4
  require 'rubygems'
5
+ require 'rubygems/package'
5
6
  require 'rake/clean'
6
7
  require 'rake/testtask'
7
8
  require 'date'
@@ -19,13 +20,13 @@ task :gem do |t|
19
20
  spec.date = Date.today.to_s
20
21
  spec.summary = "Improved logging for Ruby"
21
22
  spec.description = "Semantic Logger takes logging in Ruby to a new level by adding several new capabilities to the commonly used Logging API"
22
- spec.files = FileList["./**/*"].exclude(/.gem$/, /.log$/,/^nbproject/).map{|f| f.sub(/^\.\//, '')}
23
+ spec.files = FileList["./**/*"].exclude(/\.gem$/, /\.log$/,/nbproject/).map{|f| f.sub(/^\.\//, '')}
24
+ spec.license = "Apache License V2.0"
23
25
  spec.has_rdoc = true
24
- spec.add_dependency 'sync_attr'
25
- spec.add_dependency 'thread_safe'
26
- spec.add_development_dependency 'shoulda'
26
+ spec.add_dependency 'sync_attr', '>= 1.0'
27
+ spec.add_dependency 'thread_safe', '>= 0.1.0'
27
28
  end
28
- Gem::Builder.new(gemspec).build
29
+ Gem::Package.build gemspec
29
30
  end
30
31
 
31
32
  desc "Run Test Suite"
@@ -1,6 +1,7 @@
1
1
  # Place requires here to prevent issues on JRuby with global.require.lock=true
2
2
  require 'thread'
3
3
  require 'semantic_logger/version'
4
+ require 'semantic_logger/semantic_logger'
4
5
 
5
6
  module SemanticLogger
6
7
  autoload :Base, 'semantic_logger/base'
@@ -13,7 +14,4 @@ module SemanticLogger
13
14
  autoload :Wrapper, 'semantic_logger/appender/wrapper'
14
15
  autoload :MongoDB, 'semantic_logger/appender/mongodb'
15
16
  end
16
-
17
- # Logging levels in order with most detailed logging first
18
- LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
19
17
  end
@@ -12,15 +12,15 @@ module SemanticLogger
12
12
  # require 'semantic_logger'
13
13
  #
14
14
  # # Enable trace level logging
15
- # SemanticLogger::Logger.level = :info
15
+ # SemanticLogger.default_level = :info
16
16
  #
17
17
  # # Log to screen
18
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT)
18
+ # SemanticLogger.add_appender(STDOUT)
19
19
  #
20
20
  # # And log to a file at the same time
21
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('application.log')
21
+ # SemanticLogger::Logger.add_appender('application.log')
22
22
  #
23
- # logger = SemanticLogger::Logger.new('test')
23
+ # logger = SemanticLogger['test']
24
24
  # logger.info 'Hello World'
25
25
  #
26
26
  # Example 2. To log all levels to file and only :info and above to screen:
@@ -28,19 +28,19 @@ module SemanticLogger
28
28
  # require 'semantic_logger'
29
29
  #
30
30
  # # Enable trace level logging
31
- # SemanticLogger::Logger.level = :trace
31
+ # SemanticLogger.default_level = :trace
32
32
  #
33
33
  # # Log to screen but only display :info and above
34
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT, :info)
34
+ # SemanticLogger.add_appender(STDOUT, :info)
35
35
  #
36
36
  # # And log to a file at the same time, including all :trace level data
37
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('application.log')
37
+ # SemanticLogger.add_appender('application.log')
38
38
  #
39
- # logger = SemanticLogger::Logger.new('test')
39
+ # logger = SemanticLogger['test']
40
40
  # logger.info 'Hello World'
41
41
  #
42
42
  def initialize(filename, level=nil, &block)
43
- raise "logger cannot be null when initializing the SemanticLogging::Appender::Logger" unless filename
43
+ raise "filename cannot be null when initializing the SemanticLogging::Appender::File" unless filename
44
44
  @filename = filename
45
45
  @log = if filename.respond_to?(:write) and filename.respond_to?(:close)
46
46
  filename
@@ -13,22 +13,22 @@ module SemanticLogger
13
13
  # require 'logger'
14
14
  # require 'semantic_logger'
15
15
  # ruby_logger = Logger.new(STDOUT)
16
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::Wrapper.new(ruby_logger)
17
- # logger = SemanticLogger::Logger.new('test')
16
+ # SemanticLogger.add_appender(ruby_logger)
17
+ # logger = SemanticLogger['test']
18
18
  # logger.info('Hello World', :some => :payload)
19
19
  #
20
20
  # Enhance the Rails Logger
21
21
  # # Add the Rails logger to the list of appenders
22
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::Wrapper.new(Rails.logger)
23
- # Rails.logger = SemanticLogger::Logger.new('Rails')
22
+ # SemanticLogger.add_appender(Rails.logger)
23
+ # Rails.logger = SemanticLogger['Rails']
24
24
  #
25
25
  # # Make ActiveRecord logging include its class name in every log entry
26
- # ActiveRecord::Base.logger = SemanticLogger::Logger.new('ActiveRecord')
26
+ # ActiveRecord::Base.logger = SemanticLogger['ActiveRecord']
27
27
  #
28
28
  # Note: Since the log level is controlled by setting the Ruby or Rails logger directly
29
29
  # the level is ignored for this appender
30
30
  def initialize(logger, &block)
31
- raise "logger cannot be null when initiailizing the SemanticLogging::Appender::Logger" unless logger
31
+ raise "logger cannot be null when initiailizing the SemanticLogging::Appender::Wrapper" unless logger
32
32
  @logger = logger
33
33
 
34
34
  # Set the formatter to the supplied block
@@ -17,9 +17,9 @@ module SemanticLogger
17
17
  #
18
18
  # Note: This level is only for this particular appender. It does not override
19
19
  # the log level in any logging instance or the default log level
20
- # SemanticLogger::Logger.level
20
+ # SemanticLogger.default_level
21
21
  #
22
- # Must be one of the values in SemanticLogger::Logger::LEVELS
22
+ # Must be one of the values in SemanticLogger::LEVELS
23
23
  def level=(level)
24
24
  @level_index = self.class.map_level_to_index(level)
25
25
  @level = level
@@ -51,15 +51,15 @@ module SemanticLogger
51
51
  # require 'semantic_logger'
52
52
  #
53
53
  # # Enable trace level logging
54
- # SemanticLogger::Logger.level = :info
54
+ # SemanticLogger.default_level = :info
55
55
  #
56
56
  # # Log to screen
57
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT)
57
+ # SemanticLogger.add_appender(STDOUT)
58
58
  #
59
59
  # # And log to a file at the same time
60
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('application.log')
60
+ # SemanticLogger.add_appender('application.log')
61
61
  #
62
- # logger = SemanticLogger::Logger.new('MyApplication')
62
+ # logger = SemanticLogger['MyApplication']
63
63
  # logger.debug("Only display this if log level is set to Debug or lower")
64
64
  #
65
65
  # # Log semantic information along with a text message
@@ -204,19 +204,16 @@ module SemanticLogger
204
204
 
205
205
  # #TODO implement a thread safe #silence method
206
206
 
207
- # Initial default Level for all new instances of SemanticLogger::Logger
208
- @@default_level = :info
209
-
210
- # Allow for setting the global default log level
211
- # This change only applies to _new_ loggers, existing logger levels
212
- # will not be changed in any way
207
+ # DEPRECATED See SemanticLogger.default_level=
213
208
  def self.default_level=(level)
214
- @@default_level = level
209
+ warn "[DEPRECATION] `SemanticLogger::Logger.default_level=` is deprecated. Please use `SemanticLogger.default_level=` instead."
210
+ SemanticLogger.default_level = level
215
211
  end
216
212
 
217
- # Returns the global default log level for new Logger instances
213
+ # DEPRECATED See SemanticLogger.default_level
218
214
  def self.default_level
219
- @@default_level
215
+ warn "[DEPRECATION] `SemanticLogger::Logger.default_level` is deprecated. Please use `SemanticLogger.default_level` instead."
216
+ SemanticLogger.default_level
220
217
  end
221
218
 
222
219
  ############################################################################
@@ -224,7 +221,7 @@ module SemanticLogger
224
221
 
225
222
  def initialize(klass, level=nil)
226
223
  @name = klass.is_a?(String) ? klass : klass.name
227
- self.level = level || self.class.default_level
224
+ self.level = level || SemanticLogger.default_level
228
225
  end
229
226
 
230
227
  # Write log data to underlying data storage
@@ -10,9 +10,11 @@ require 'sync_attr'
10
10
  # Example
11
11
  #
12
12
  # require 'semantic_logger'
13
+ # SemanticLogger.default_level = :debug
14
+ # SemanticLogger.add_appender(STDOUT)
13
15
  #
14
16
  # class ExternalSupplier
15
- # # Lazy load 'logger' class variable on first use
17
+ # # Create class and instance logger methods
16
18
  # include SemanticLogger::Loggable
17
19
  #
18
20
  # def call_supplier(amount, name)
@@ -33,14 +35,64 @@ module SemanticLogger
33
35
  include SyncAttr
34
36
 
35
37
  sync_cattr_reader :logger do
36
- SemanticLogger::Logger.new(self)
38
+ SemanticLogger[self]
37
39
  end
38
40
  end
39
41
  end
40
42
 
41
43
  # Also make the logger available as an instance method MixIn
44
+ # The class logger can be replaced using an instance specific #logger= below
42
45
  def logger
43
- self.class.logger
46
+ @semantic_logger ||= self.class.logger
47
+ end
48
+
49
+ # Set instance specific logger
50
+ #
51
+ # By default instances of the class will use the class logger. Sometimes it
52
+ # is useful to be able to add instance specific logging data to the class name.
53
+ #
54
+ # For example, server or host_name that the class instance is using.
55
+ #
56
+ # Example:
57
+ # require 'semantic_logger'
58
+ # SemanticLogger.default_level = :debug
59
+ # SemanticLogger.add_appender(STDOUT)
60
+ #
61
+ # class MyClass
62
+ # include SemanticLogger::Loggable
63
+ #
64
+ # def self.my_name=(my_name)
65
+ # # Use class level logger that only logs class name
66
+ # logger.info "My name is changed to #{my_name}"
67
+ #
68
+ # @@my_name = my_name
69
+ # end
70
+ #
71
+ # def initialize(host_name)
72
+ # # Add host_name to every log entry in this logging instance
73
+ # self.logger = SemanticLogger["#{self.class.name} [#{host_name}]"]
74
+ #
75
+ # logger.info "Started server"
76
+ # end
77
+ #
78
+ # def check
79
+ # logger.debug "Checking..."
80
+ # end
81
+ # end
82
+ #
83
+ # MyClass.my_name = "Joe"
84
+ #
85
+ # mine = MyClass.new('server.com')
86
+ # mine.check
87
+ #
88
+ # # Generates the following log output:
89
+ #
90
+ # 2013-04-02 15:08:42.368574 I [37279:70198560687720] MyClass -- My name is changed to Joe
91
+ # 2013-04-02 15:08:42.369934 I [37279:70198560687720] MyClass [server.com] -- Started server
92
+ # 2013-04-02 15:08:42.371171 D [37279:70198560687720] MyClass [server.com] -- Checking...
93
+ #
94
+ def logger=(logger)
95
+ @semantic_logger = logger
44
96
  end
45
97
 
46
98
  end
@@ -1,43 +1,9 @@
1
1
  require 'thread_safe'
2
2
 
3
- # Logger is the interface used by
4
- #
5
- # Logger maintains the logging name to be used for all log entries generated
6
- # by the invoking classes or modules
7
- #
8
- # It is recommended to create an instance of the class for every class or
9
- # module so that it can be uniquely identified and searched on
10
- #
11
- # Example, log to Logger:
12
- # require 'logger'
13
- # require 'semantic_logger'
14
- # log = Logger.new(STDOUT)
15
- # log.level = Logger::DEBUG
16
- #
17
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::Logger.new(log)
18
- #
19
- # logger = SemanticLogger::Logger.new("my.app.class")
20
- # logger.debug("Login time", :user => 'Joe', :duration => 100, :ip_address=>'127.0.0.1')
21
- #
22
- # # Now log to the Logger above as well as MongoDB at the same time
23
- #
24
- # db = Mongodb::Connection.new['production_logging']
25
- #
26
- # SemanticLogger::Logger.appenders << SemanticLogger::Appender::MongoDB.new(
27
- # :db => db,
28
- # :collection_size => 25.gigabytes
29
- # )
30
- # ...
31
- # # This will be logged to both the Ruby Logger and MongoDB
32
- # logger.debug("Login time", :user => 'Mary', :duration => 230, :ip_address=>'192.168.0.1')
33
- #
3
+ # Logger stores the class name to be used for all log messages so that every
4
+ # log message written by this instance will include the class name
34
5
  module SemanticLogger
35
6
  class Logger < Base
36
- # Add or remove logging appenders to the thread-safe appenders Array
37
- # Appenders will be written to in the order that they appear in this list
38
- def self.appenders
39
- @@appenders
40
- end
41
7
 
42
8
  # Returns a Logger instance
43
9
  #
@@ -53,11 +19,11 @@ module SemanticLogger
53
19
  #
54
20
  # level
55
21
  # The initial log level to start with for this logger instance
56
- # Default: SemanticLogger::Logger.default_level
22
+ # Default: SemanticLogger.default_level
57
23
  #
58
24
  def initialize(klass, level=nil)
59
25
  @name = klass.is_a?(String) ? klass : klass.name
60
- self.level = level || self.class.default_level
26
+ self.level = level || SemanticLogger.default_level
61
27
  end
62
28
 
63
29
  # Returns [Integer] the number of log entries that have not been written
@@ -71,12 +37,6 @@ module SemanticLogger
71
37
  queue.size
72
38
  end
73
39
 
74
- # DEPRECATED: Please use queue_size instead.
75
- def self.cache_count
76
- warn "[DEPRECATION] 'SemanticLogger::Logger.cache_count' is deprecated. Please use 'SemanticLogger::Logger.queue_size' instead."
77
- queue_size
78
- end
79
-
80
40
  # Flush all queued log entries disk, database, etc.
81
41
  # All queued log messages are written and then each appender is flushed in turn
82
42
  def self.flush
@@ -121,11 +81,21 @@ module SemanticLogger
121
81
  @@logger = logger
122
82
  end
123
83
 
84
+ # DEPRECATED See SemanticLogger.add_appender
85
+ def self.appenders
86
+ warn "[DEPRECATION] `SemanticLogger::Logger.appenders` is deprecated. Please use `SemanticLogger.add_appender` instead."
87
+ SemanticLogger.appenders
88
+ end
89
+
90
+ # DEPRECATED: Please use queue_size instead.
91
+ def self.cache_count
92
+ warn "[DEPRECATION] 'SemanticLogger::Logger.cache_count' is deprecated. Please use 'SemanticLogger::Logger.queue_size' instead."
93
+ queue_size
94
+ end
124
95
 
125
96
  ############################################################################
126
97
  protected
127
98
 
128
- @@appenders = ThreadSafe::Array.new
129
99
  @@appender_thread = nil
130
100
  @@queue = Queue.new
131
101
 
@@ -177,7 +147,7 @@ module SemanticLogger
177
147
  count = 0
178
148
  while message = queue.pop
179
149
  if message.is_a? Log
180
- appenders.each do |appender|
150
+ SemanticLogger.appenders.each do |appender|
181
151
  begin
182
152
  appender.log(message)
183
153
  rescue Exception => exc
@@ -195,7 +165,7 @@ module SemanticLogger
195
165
  else
196
166
  case message[:command]
197
167
  when :flush
198
- appenders.each do |appender|
168
+ SemanticLogger.appenders.each do |appender|
199
169
  begin
200
170
  logger.info "Appender thread: Flushing appender: #{appender.name}"
201
171
  appender.flush
@@ -0,0 +1,135 @@
1
+ require 'thread_safe'
2
+ module SemanticLogger
3
+ # Logging levels in order of most detailed to most severe
4
+ LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
5
+
6
+ # Return a logger for the supplied class or class_name
7
+ def self.[](klass)
8
+ SemanticLogger::Logger.new(klass)
9
+ end
10
+
11
+ # Allow for setting the global default log level
12
+ # This change only applies to _new_ loggers, existing logger levels
13
+ # will not be changed in any way
14
+ def self.default_level=(level)
15
+ @@default_level = level
16
+ end
17
+
18
+ # Returns the global default log level for new Logger instances
19
+ def self.default_level
20
+ @@default_level
21
+ end
22
+
23
+ # Add a new logging appender as a new destination for all log messages
24
+ # emitted from Semantic Logger
25
+ #
26
+ # Appenders will be written to in the order that they are added
27
+ #
28
+ # If a block is supplied then it will be used to customize the format
29
+ # of the messages sent to that appender. See SemanticLogger::Logger.new for
30
+ # more information on custom formatters
31
+ #
32
+ # Parameters
33
+ # appender [String|IO|SemanticLogger::Appender::Base|::Logger]
34
+ # Filename to write log messages to
35
+ # Or,
36
+ # STDOUT, STDERR, or any IO stream to write log messages to
37
+ # Or,
38
+ # Any SemanticLogger::Appender instance such as
39
+ # SemanticLogger::Appender::File
40
+ # SemanticLogger::Appender::Wrapper
41
+ # SemanticLogger::Appender::Mongodb
42
+ # Or,
43
+ # A custom appender derived from SemanticLogger::Appender::Base
44
+ # Or,
45
+ # Ruby built-in Logger, or any logger that implements the following methods:
46
+ # :debug, :info, :warn, :error, :fatal
47
+ #
48
+ # log_level [Symbol]
49
+ # Optional
50
+ # By setting the log_level higher than the SemanticLogger::default_level
51
+ # this appender can exclude lower level log messages
52
+ # Any one of SemanticLogger::LEVELS. For example: :trace, :debug, :info, :warn, :error, :fatal
53
+ #
54
+ # Examples:
55
+ #
56
+ # # Send all logging output to Standard Out (Screen)
57
+ # SemanticLogger.add_appender(STDOUT)
58
+ #
59
+ # # Send all logging output to a file
60
+ # SemanticLogger.add_appender('logfile.log')
61
+ #
62
+ # # Send all logging output to a file and only :info and above to standard output
63
+ # SemanticLogger.add_appender('logfile.log')
64
+ # SemanticLogger.add_appender(STDOUT, :info)
65
+ #
66
+ # Log to an existing logger:
67
+ #
68
+ # # Send Semantic logging output to an existing logger
69
+ # require 'logger'
70
+ # require 'semantic_logger'
71
+ #
72
+ # # Built-in Ruby logger
73
+ # log = Logger.new(STDOUT)
74
+ # log.level = Logger::DEBUG
75
+ #
76
+ # SemanticLogger.default_level = :debug
77
+ # SemanticLogger.add_appender(log)
78
+ #
79
+ # logger = SemanticLogger['Example']
80
+ # logger.info "Hello World"
81
+ # logger.debug("Login time", :user => 'Joe', :duration => 100, :ip_address=>'127.0.0.1')
82
+ #
83
+ def self.add_appender(appender, log_level=nil, &block)
84
+ appender_instance = if appender.is_a?(String) || appender.is_a?(IO)
85
+ # $stderr, STDOUT, other IO, or a filename
86
+ SemanticLogger::Appender::File.new(appender, log_level, &block)
87
+ elsif appender.is_a? Appender::Base
88
+ # Already an instance of an appender
89
+ appender.log_level = log_level if log_level
90
+ appender.formatter = block if block
91
+ appender
92
+ else
93
+ # Check if the custom appender responds to all the log levels. For example Ruby ::Logger
94
+ if does_not_implement = LEVELS[1..-1].find{|i| !appender.respond_to?(i)}
95
+ raise "Supplied appender does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}"
96
+ end
97
+
98
+ raise "Change the log level to #{log_level}, update the log level directly against the supplied appender" if log_level
99
+ SemanticLogger::Appender::Wrapper.new(appender, &block)
100
+ end
101
+ @@appenders << appender_instance
102
+ end
103
+
104
+ # Remove an existing appender
105
+ # Currently only supports appender instances
106
+ # TODO allow removing by filename, STDOUT etc..
107
+ def self.remove_appender(appender)
108
+ @@appenders.delete(appender)
109
+ end
110
+
111
+ # Returns [SemanticLogger::Appender::Base] a copy of the list of active
112
+ # appenders for debugging etc.
113
+ # Use SemanticLogger.add_appender and SemanticLogger.remove_appender
114
+ # to manipulate the active appenders list
115
+ def self.appenders
116
+ @@appenders.dup
117
+ end
118
+
119
+ # Wait until all queued log messages have been written and flush all active
120
+ # appenders
121
+ def self.flush
122
+ SemanticLogger::Logger.flush
123
+ end
124
+
125
+ ############################################################################
126
+ protected
127
+
128
+ @@appenders = ThreadSafe::Array.new
129
+
130
+ ############################################################################
131
+ private
132
+
133
+ # Initial default Level for all new instances of SemanticLogger::Logger
134
+ @@default_level = :info
135
+ end