-
Notifications
You must be signed in to change notification settings - Fork 63
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
Conversation
lib/logger.rb
Outdated
# logger.with_level(:debug) do | ||
# logger.debug { "Hello" } | ||
# end | ||
def with_level(severity, &block) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative name: at
?
log.at(:debug) do
log.debug("You'll see this")
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think with_level
is much more clear what is going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&block
is not needed, since you are only yielding and not capturing.
Sorry, I did not see this. Please mention me or add me as a reviewer in the future, otherwise I don't know about it :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with the idea, but I'm against pollute Thread.current
with this. Instead, add a instance variable storing a hash inside the logger keyed by Thread.current
. This approach also makes it simple to change from a thread-local implementation to a fiber-local or global implementation by changing the key.
lib/logger.rb
Outdated
# logger.with_level(:debug) do | ||
# logger.debug { "Hello" } | ||
# end | ||
def with_level(severity, &block) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&block
is not needed, since you are only yielding and not capturing.
Thanks @jeremyevans, great review. What do you think about using It might make sense just to leave this as implementation dependent so that other loggers (e.g. the ones I maintain) can be fiber-specific or use fiber-storage instead. |
Ok, refactored based on feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good. One more implementation change requested.
Co-authored-by: Samuel Williams <[email protected]>
Sorry, one more change is required, we will need to add |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of small things that could be fixed, but I don't think need to block merging.
logger.with_level(DEBUG) do # verify reentrancy | ||
assert_equal(logger.level, DEBUG) | ||
|
||
Thread.new do |
There was a problem hiding this comment.
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.
Really? irb 3.2 works fine without it:
|
According to CI, |
Right, looks like 3.1+ doesn't require "fiber" anymore. |
(ruby/logger#85) * Update lib/logger/severity.rb ruby/logger@7aabb0b4aa
Thanks @mperham - I want to make one more PR and then we will try to cut a release. |
This commit introduces |
ruby/logger#85 added this to the `Logger` initializer. Since we don't call `super` in `initialize`, we need to do it ourselves. Not doing this leads to an error in `Logger#level`: # undefined method `[]' for nil:NilClass (NoMethodError): def level @level_override[Fiber.current] || @Level # ^^^^^^^^^^^^^^^ end
ruby/logger#85 added this to the `Logger` initializer. Since we don't call `super` in `initialize`, we need to do it ourselves. Not doing this leads to an error in `Logger#level`: ```ruby def level @level_override[Fiber.current] || @Level # ^^^^^^^^^^^^^^^ end ```
ruby/logger#85 added `@level_override` to the `Logger` initializer. Since we didn't call `super` in `initialize`, the variable was uninitialized, and we would run into a `NoMethodError` in `Logger#level` on Ruby HEAD: def level @level_override[Fiber.current] || @Level # ^^^^^^^^^^^^^^^ NoMethodError: undefined method `[]' for nil:NilClass end
logger 1.4.3 will have ruby/logger#85. It initializes a new instance variable in `Logger#initialize`. `Jekyll::Stevenson < ::Logger` doesn't use `super`. So the new instance variable isn't initialized in `Jekyll::Stevenson`. And it causes an exception: ```text /tmp/local/lib/ruby/3.3.0+0/logger.rb:385:in `level': undefined method `[]' for nil (NoMethodError) @level_override[Fiber.current] || @Level ^^^^^^^^^^^^^^^ from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/log_adapter.rb:45:in `adjust_verbosity' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/configuration.rb:143:in `config_files' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll.rb:118:in `configuration' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/command.rb:44:in `configuration_from_options' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/commands/serve.rb:83:in `block (2 levels) in init_with_program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `block in execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `each' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in `go' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary.rb:21:in `program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/exe/jekyll:15:in `<top (required)>' from /tmp/local/bin/jekyll:25:in `load' from /tmp/local/bin/jekyll:25:in `<top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `kernel_load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:23:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:492:in `exec' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/command.rb:27:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:34:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/base.rb:485:in `start' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:28:in `start' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:37:in `block in <top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/friendly_errors.rb:117:in `with_friendly_errors' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:29:in `<top (required)>' from /tmp/local/bin/bundle:25:in `load' from /tmp/local/bin/bundle:25:in `<main>' ``` How about using `super` instead of implementing our `Jekyll::Stevenson#initialize` to reduce maintenance cost?
logger 1.4.3 will have ruby/logger#85. It initializes a new instance variable in `Logger#initialize`. `Jekyll::Stevenson < ::Logger` doesn't use `super`. So the new instance variable isn't initialized in `Jekyll::Stevenson`. And it causes an exception: ```text /tmp/local/lib/ruby/3.3.0+0/logger.rb:385:in `level': undefined method `[]' for nil (NoMethodError) @level_override[Fiber.current] || @Level ^^^^^^^^^^^^^^^ from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/log_adapter.rb:45:in `adjust_verbosity' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/configuration.rb:143:in `config_files' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll.rb:118:in `configuration' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/command.rb:44:in `configuration_from_options' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/commands/serve.rb:83:in `block (2 levels) in init_with_program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `block in execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `each' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in `go' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary.rb:21:in `program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/exe/jekyll:15:in `<top (required)>' from /tmp/local/bin/jekyll:25:in `load' from /tmp/local/bin/jekyll:25:in `<top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `kernel_load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:23:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:492:in `exec' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/command.rb:27:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:34:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/base.rb:485:in `start' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:28:in `start' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:37:in `block in <top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/friendly_errors.rb:117:in `with_friendly_errors' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:29:in `<top (required)>' from /tmp/local/bin/bundle:25:in `load' from /tmp/local/bin/bundle:25:in `<main>' ``` How about using `super` instead of implementing our `Jekyll::Stevenson#initialize` to reduce maintenance cost?
logger 1.4.3 will have ruby/logger#85. It initializes a new instance variable in `Logger#initialize`. `Jekyll::Stevenson < ::Logger` doesn't use `super`. So the new instance variable isn't initialized in `Jekyll::Stevenson`. And it causes an exception: ```text /tmp/local/lib/ruby/3.3.0+0/logger.rb:385:in `level': undefined method `[]' for nil (NoMethodError) @level_override[Fiber.current] || @Level ^^^^^^^^^^^^^^^ from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/log_adapter.rb:45:in `adjust_verbosity' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/configuration.rb:143:in `config_files' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll.rb:118:in `configuration' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/command.rb:44:in `configuration_from_options' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/lib/jekyll/commands/serve.rb:83:in `block (2 levels) in init_with_program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `block in execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `each' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `execute' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in `go' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/mercenary-0.4.0/lib/mercenary.rb:21:in `program' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/jekyll-4.3.2/exe/jekyll:15:in `<top (required)>' from /tmp/local/bin/jekyll:25:in `load' from /tmp/local/bin/jekyll:25:in `<top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:58:in `kernel_load' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli/exec.rb:23:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:492:in `exec' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/command.rb:27:in `run' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:34:in `dispatch' from /tmp/local/lib/ruby/3.3.0+0/bundler/vendor/thor/lib/thor/base.rb:485:in `start' from /tmp/local/lib/ruby/3.3.0+0/bundler/cli.rb:28:in `start' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:37:in `block in <top (required)>' from /tmp/local/lib/ruby/3.3.0+0/bundler/friendly_errors.rb:117:in `with_friendly_errors' from /tmp/local/lib/ruby/gems/3.3.0+0/gems/bundler-2.5.0.dev/libexec/bundle:29:in `<top (required)>' from /tmp/local/bin/bundle:25:in `load' from /tmp/local/bin/bundle:25:in `<main>' ``` How about using `super` instead of implementing our `Jekyll::Stevenson#initialize` to reduce maintenance cost?
Generally speaking, this breaks any logger subclass that does not call super in its initializer. |
I don't believe adding additional complexity to work around 3rd party bugs is a good idea. Not calling super is a bug, no? |
Hey @ioquatix, did that other PR ever land? I'd love to see this released so we could potentially use it to replace the |
@skipkayhil FYI this has been released now. |
I believe we need something like this to replace |
This adds Logger#with_level which provides a thread-local log level so Threads can temporarily enable logging for a given block.
Fixes #84