Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add thread-local log level #85

Merged
merged 6 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# HEAD

* Add thread-local log levels using `Logger#with_level` [#84](https://github.com/ruby/logger/issue/84)
mperham marked this conversation as resolved.
Show resolved Hide resolved

# 1.4.2

* Document that shift_age of 0 disables log file rotation [#43](https://github.com/ruby/logger/pull/43) (thanks to jeremyevans)
Expand Down
38 changes: 18 additions & 20 deletions lib/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,9 @@ class Logger
include Severity

# Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
attr_reader :level
def level
@tll[Thread.current] || @level
mperham marked this conversation as resolved.
Show resolved Hide resolved
end

# Sets the log level; returns +severity+.
# See {Log Level}[rdoc-ref:Logger@Log+Level].
Expand All @@ -395,25 +397,20 @@ class Logger
# Logger#sev_threshold= is an alias for Logger#level=.
#
def level=(severity)
if severity.is_a?(Integer)
@level = severity
else
case severity.to_s.downcase
when 'debug'
@level = DEBUG
when 'info'
@level = INFO
when 'warn'
@level = WARN
when 'error'
@level = ERROR
when 'fatal'
@level = FATAL
when 'unknown'
@level = UNKNOWN
else
raise ArgumentError, "invalid log level: #{severity}"
end
@level = Severity.coerce(severity)
end

# Adjust the log level during the block execution for the current Thread only
#
# logger.with_level(:debug) do
# logger.debug { "Hello" }
# end
def with_level(severity)
current, @tll[Thread.current] = level, Severity.coerce(severity)
begin
yield
ensure
@tll[Thread.current] = current
end
end

Expand Down Expand Up @@ -583,6 +580,7 @@ def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
self.datetime_format = datetime_format
self.formatter = formatter
@logdev = nil
@tll = ObjectSpace::WeakMap.new # thread-local levels
mperham marked this conversation as resolved.
Show resolved Hide resolved
if logdev && logdev != File::NULL
@logdev = LogDevice.new(logdev, shift_age: shift_age,
shift_size: shift_size,
Expand Down
19 changes: 19 additions & 0 deletions lib/logger/severity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,24 @@ module Severity
FATAL = 4
# An unknown message that should always be logged.
UNKNOWN = 5

LEVELS = {
"debug" => DEBUG,
"info" => INFO,
"warn" => WARN,
"error" => ERROR,
"fatal" => FATAL,
"unknown" => UNKNOWN,
}
private_constant :LEVELS

def self.coerce(severity)
if severity.is_a?(Integer)
severity
else
k = severity.to_s.downcase
LEVELS[k] || raise(ArgumentError, "invalid log level: #{severity}")
mperham marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
end
32 changes: 32 additions & 0 deletions test/logger/test_severity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
require 'logger'

class TestLoggerSeverity < Test::Unit::TestCase
include Logger::Severity

def test_enum
logger_levels = Logger.constants
levels = ["WARN", "UNKNOWN", "INFO", "FATAL", "DEBUG", "ERROR"]
Expand All @@ -23,4 +25,34 @@ def test_level_assignment
assert(logger.level) == Logger::Severity.const_get(level)
end
end

def test_thread_local_level
logger = Logger.new(nil)
logger.level = INFO # default level
other = Logger.new(nil)
other.level = ERROR # default level

assert_equal(other.level, ERROR)
logger.with_level(:WARN) do
assert_equal(other.level, ERROR)
assert_equal(logger.level, WARN)

logger.with_level(DEBUG) do # verify reentrancy
assert_equal(logger.level, DEBUG)

Thread.new do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests for Thread-local, not explicitly for Fiber-local, but works since a new Thread is also a new Fiber. Not sure if we want to explicitly test for Fiber-localness. Could be done later.

assert_equal(logger.level, INFO)
logger.with_level(:WARN) do
assert_equal(other.level, ERROR)
assert_equal(logger.level, WARN)
end
assert_equal(logger.level, INFO)
end.join

assert_equal(logger.level, DEBUG)
end
assert_equal(logger.level, WARN)
end
assert_equal(logger.level, INFO)
end
end