semantic_logger 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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