Permalink
Browse files

Topic: merge Better semantics for set_log_level. (add topic merge)

set_log_level now has the following semantics:
- All loggers matching the supplied regex will be updated to the new level
- Any old loggers who were at the level, and whose name does not match the given regex, will be set to the default level
- Future calls to VCAP::Logging.logger will set the level on newly created loggers according to all configured regexs.
  The regexs are consulted in decreasing order of strictness, stopping at the first one that matches.

Test plan:
Added unit tests that exercise the new functionality.

Change-Id: Id7e0dc19c33cd19815f01c8c81019e2eb25fa0d9
  • Loading branch information...
1 parent 1c25415 commit 1f935f96577864112d5b159643e6f95982efc825 mpage committed Jun 7, 2011
Showing with 40 additions and 33 deletions.
  1. +1 −8 vcap_logging/README.md
  2. +25 −19 vcap_logging/lib/vcap/logging.rb
  3. +14 −6 vcap_logging/spec/unit/logging_spec.rb
View
9 vcap_logging/README.md
@@ -25,11 +25,4 @@ and sockets).
### Logger
Loggers are responsible for dispatching messages that need to be logged off to
-their appropriate sinks. They are arranged in a tree structure by name, such
-that log records flow from the leaves of the tree towards the root. The log
-record stops traversing the tree when it arrives at a logger that has been
-configured with a sink.
-
-### Trie
-
-Internally, a trie is used to manage the logger hierarchy.
+their appropriate sinks.
View
44 vcap_logging/lib/vcap/logging.rb
@@ -30,42 +30,38 @@ class << self
# :delimiter => String Defines how names should be split in determining logger hierarchy
#
def init(opts={})
+ @delimiter = opts[:delimiter] || DEFAULT_DELIMITER
@log_level_map = opts[:log_levels] || DEFAULT_LOG_LEVELS
- @delimiter = opts[:delimiter] || DEFAULT_DELIMITER
if opts[:default_log_level]
@default_log_level = opts[:default_log_level]
else
- # The middle level seems like a reasonable default for the root logger
+ # The middle level seems like a reasonable default level
sorted_levels = @log_level_map.keys.sort {|a, b| @log_level_map[a] <=> @log_level_map[b] }
@default_log_level = sorted_levels[sorted_levels.length / 2]
end
VCAP::Logging::Logger.define_log_levels(@log_level_map)
@sink_map = VCAP::Logging::SinkMap.new(@log_level_map)
+ @log_level_filters = {} # map of level => regex that specifies which loggers should be at that level
+ @sorted_log_level_filters = [] # [[level, filter]] sorted by level strictness, most strict first
@loggers = {}
end
- # Returns the logger associated with _name_. Creates one if it doesn't exist. The log level will be inherited
- # from the parent logger.
+ # Returns the logger associated with _name_. Creates one if it doesn't exist. The log level is computed
+ # by checking the masks set using VCAP::Logging.set_log_level in order from most restrictive to
+ # least restrictive.
#
# @param name String Logger name
def logger(name)
if !@loggers.has_key?(name)
@loggers[name] = VCAP::Logging::Logger.new(name, @sink_map)
-
- # Not super efficient, but since we're not explicitly storing the parent-child relationships we
- # must brute force it.
- log_level = @default_log_level
- off = name.rindex(@delimiter)
- while off != nil
- substr = name[0, off]
- if @loggers[substr]
- log_level = @loggers[substr].log_level
+ @loggers[name].log_level = @default_log_level
+ for level, regex in @sorted_log_level_filters
+ if regex.match(name)
+ @loggers[name].log_level = level
break
end
- off = off > 2 ? name.rindex(@delimiter, off - 1) : nil
end
- @loggers[name].log_level = log_level
end
@loggers[name]
@@ -75,7 +71,9 @@ def add_sink(*args)
@sink_map.add_sink(*args)
end
- # Sets the log level to _log_level_ for every logger whose name matches _path_regex_
+ # Sets the log level to _log_level_ for every logger whose name matches _path_regex_. Loggers who
+ # were previously set to this level and whose names no longer match _path_regex_ are reset to
+ # the default level.
#
# @param path_regex String Regular expression to use when matching against the logger name
# @param log_level_name Symbol Name of the log level to set on all matching loggers
@@ -85,13 +83,21 @@ def set_log_level(path_regex, log_level_name)
raise ArgumentError, "Unknown log level #{log_level_name}" unless @log_level_map[log_level_name]
regex = Regexp.new("^#{path_regex}$")
+ @log_level_filters[log_level_name] = regex
+ @sorted_log_level_filters = @log_level_filters.keys.sort {|a, b| @log_level_map[a] <=> @log_level_map[b] }.map {|lvl| [lvl, @log_level_filters[lvl]] }
+
for logger_name, logger in @loggers
- logger.log_level = log_level_name if regex.match(logger_name)
+ if regex.match(logger_name)
+ logger.log_level = log_level_name
+ elsif logger.log_level == log_level_name
+ # Reset any loggers at the supplied level that no longer match
+ logger.log_level = @default_log_level
+ end
end
end
- end
- end
+ end # << self
+ end # VCAP::Logging
end
require 'vcap/logging/formatter'
View
20 vcap_logging/spec/unit/logging_spec.rb
@@ -26,17 +26,17 @@
VCAP::Logging.logger('foo.bar').should == logger
end
- it 'should set log level to the default for loggers with no parents' do
+ it 'should set log level to the default if no masks are present' do
logger = VCAP::Logging.logger('foo.bar')
logger.should_not be_nil
logger.log_level.should == VCAP::Logging.default_log_level
end
- it 'should set the log level to that of its parent' do
- VCAP::Logging.default_log_level = :warn
- VCAP::Logging.logger('foo').log_level.should == :warn
- VCAP::Logging.logger('foo').log_level = :info
- VCAP::Logging.logger('foo.bar').log_level.should == :info
+ it 'should set log level to the most restrictive mask that matches' do
+ VCAP::Logging.set_log_level('.*', :debug)
+ VCAP::Logging.set_log_level('foo\..*', :fatal)
+ logger = VCAP::Logging.logger('foo.bar')
+ logger.log_level.should == :fatal
end
end
@@ -63,6 +63,14 @@
VCAP::Logging.logger(name).log_level.should == levels[1]
end
end
+
+ it 'should reset loggers at a given level that no longer match to the default level' do
+ logger = VCAP::Logging.logger('foo.bar')
+ VCAP::Logging.set_log_level('foo.bar', :warn)
+ logger.log_level.should == :warn
+ VCAP::Logging.set_log_level('zazzle', :warn)
+ logger.log_level.should == VCAP::Logging.default_log_level
+ end
end
end

0 comments on commit 1f935f9

Please sign in to comment.