lib/semantic_logger/base.rb in semantic_logger-2.7.0 vs lib/semantic_logger/base.rb in semantic_logger-2.8.0

- old
+ new

@@ -7,26 +7,31 @@ # Note: Do not create instances of this class directly # module SemanticLogger class Base # Class name to be logged - attr_accessor :name + attr_accessor :name, :filter - attr_reader :level - # Set the logging level for this logger # - # Note: This level is only for this particular appender. It does not override + # Note: This level is only for this particular instance. It does not override # the log level in any logging instance or the default log level # SemanticLogger.default_level # - # Must be one of the values in SemanticLogger::LEVELS + # Must be one of the values in SemanticLogger::LEVELS, or + # nil if this logger instance should use the global default log_level def level=(level) - @level_index = self.class.map_level_to_index(level) + @level_index = SemanticLogger.level_to_index(level) @level = level end + # Returns the current log level if set, otherwise it returns the global + # default log level + def level + @level || SemanticLogger.default_level + end + # Implement the log level calls # logger.debug(message, hash|exception=nil, &block) # # Implement the log level query # logger.debug? @@ -67,34 +72,44 @@ # # # Log an exception in a semantic way # logger.info("Parsing received XML", exc) # SemanticLogger::LEVELS.each_with_index do |level, index| - class_eval <<-EOT, __FILE__, __LINE__ + class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{level}(message=nil, payload=nil, exception=nil, &block) - if @level_index <= #{index} + if level_index <= #{index} log_internal(:#{level}, #{index}, message, payload, exception, &block) true else false end end def #{level}? - @level_index <= #{index} + level_index <= #{index} end def benchmark_#{level}(message, params = {}, &block) - if @level_index <= #{index} + if level_index <= #{index} benchmark_internal(:#{level}, #{index}, message, params, &block) else block.call(params) if block end end EOT end + # Dynamically supply the log level with every benchmark call + def benchmark(level, message, params = {}, &block) + index = SemanticLogger.level_to_index(level) + if level_index <= index + benchmark_internal(level, index, message, params, &block) + else + block.call(params) if block + end + end + # Add the supplied tags to the list of tags to log for this thread whilst # the supplied block is active # Returns nil if no tags are currently set # To support: ActiveSupport::TaggedLogging V3 and above def tagged(*tags) @@ -177,22 +192,48 @@ end ############################################################################ protected - def initialize(klass, level=nil) - @name = klass.is_a?(String) ? klass : klass.name - self.level = level || SemanticLogger.default_level + # Initializer for Abstract Class SemanticLogger::Base + # + # Parameters + # klass [String] + # Name of the class, module, or other identifier for which the log messages + # are being logged + # + # level [Symbol] + # Only allow log entries of this level or higher to be written to this appender + # For example if set to :warn, this appender would only log :warn and :fatal + # log messages when other appenders could be logging :info and lower + # + # filter [Regexp|Proc] + # RegExp: Only include log messages where the class name matches the supplied + # regular expression. All other messages will be ignored + # Proc: Only include log messages where the supplied Proc returns true + # The Proc must return true or false + def initialize(klass, level=nil, filter=nil) + # Support filtering all messages to this logger using a Regular Expression + # or Proc + raise ":filter must be a Regexp or Proc" unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc) + + @filter = filter.is_a?(Regexp) ? filter.freeze : filter + @name = klass.is_a?(String) ? klass : klass.name + self.level = level unless level.nil? end # Write log data to underlying data storage def log(log_) raise NotImplementedError.new("Logging Appender must implement #log(log)") end # Return the level index for fast comparisons - attr_reader :level_index + # Returns the global default level index if the level has not been explicitly + # set for this instance + def level_index + @level_index || SemanticLogger.default_level_index + end # Struct Log # # level # Log level of the supplied log call @@ -224,31 +265,19 @@ # # metric [Object] # Object supplied when benchmark_x was called Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric) - # Internal method to return the log level as an internal index - # Also supports mapping the ::Logger levels to SemanticLogger levels - def self.map_level_to_index(level) - index = if level.is_a?(Integer) && defined?(::Logger::Severity) - # Mapping of Rails and Ruby Logger levels to SemanticLogger levels - @@map_levels ||= begin - levels = [] - ::Logger::Severity.constants.each do |constant| - levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error) - end - levels - end - @@map_levels[level] - elsif level.is_a?(String) - level = level.downcase.to_sym - LEVELS.index(level) - else - LEVELS.index(level) + # Whether to log the supplied message based on the current filter if any + def include_message?(struct) + return true if @filter.nil? + + if @filter.is_a?(Regexp) + (@filter =~ struct.name) != nil + elsif @filter.is_a?(Proc) + @filter.call(struct) == true end - raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index - index end # Log message at the specified level def log_internal(level, index, message=nil, payload=nil, exception=nil, &block) if exception.nil? && payload && payload.is_a?(Exception) @@ -268,11 +297,12 @@ # Add scoped payload if self.payload payload = payload.nil? ? self.payload : self.payload.merge(payload) end - log Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception) + struct = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception) + log(struct) if include_message?(struct) end # Measure the supplied block and log the message def benchmark_internal(level, index, message, params, &block) start = Time.now @@ -300,17 +330,20 @@ payload = payload.nil? ? self.payload : self.payload.merge(payload) end if exception case log_exception when :full - log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, exception, metric) + struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, exception, metric) + log(struct) if include_message?(struct) when :partial - log Log.new(level, Thread.current.name, name, "#{message} -- Exception: #{exception.class}: #{exception.message}", payload, end_time, duration, tags, index, nil, metric) + struct = Log.new(level, Thread.current.name, name, "#{message} -- Exception: #{exception.class}: #{exception.message}", payload, end_time, duration, tags, index, nil, metric) + log(struct) if include_message?(struct) end raise exception elsif duration >= min_duration # Only log if the block took longer than 'min_duration' to complete - log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric) + struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric) + log(struct) if include_message?(struct) end end end end