Closed eviljoel closed 1 year ago
Since the appender list is global, can I ask about the use-case to make SemanticLogger.add_appender
and SemanticLogger.clear_appenders!
thread-safe?
These methods are usually called once during process startup to setup the appenders.
Since the appender list is global, can I ask about the use-case to make
SemanticLogger.add_appender
andSemanticLogger.clear_appenders!
thread-safe?
@reidmorrison, I don't have a real life use case. However, you do make very specific claims about thread safety in the documentation and on the project website. From docs/index.md
(emphasis added):
Semantic Logger is completely thread safe and all methods can be called concurrently from any thread.
It would be perfectly acceptable to make a documentation update instead. You could say something like "Semantic Logger is completely thread safe. Unless otherwise noted, all methods can be called concurrently from any thread." and update the method documentation to explicitly state which methods are not thread safe.
I do think a change needs to be made as the documentation is currently not consistent with the functionality.
I want to point out that I do have a real life use case for PR 236. In that case, we are adding and removing appenders between every unit test.
I'd also like to point out that I did not change the calls that actually do the logging (info, warn, error, etc.), so performance of those calls should not be affected.
To help with this process, I completely refactored the way Async and BatchAsync are handled. This should make it much easier to locate the methods that would need semaphore protection: https://github.com/reidmorrison/semantic_logger/pull/242
Also added documentation on how to test whether application code is logging correctly. See the section "Writing Tests" at the end of the guide: https://logger.rocketjob.io/api.html
We can include these changes as part of a new major release of Semantic Logger, that will also raise the minimum ruby version to 2.7.
@eviljoel can you try this branch and let me know how it goes for you?
Inside Gemfile:
gem "semantic_logger", github: "reidmorrison/semantic_logger", branch: "feature/refactor-async"
@eviljoel can you try this branch and let me know how it goes for you?
Well, it definitely is not thread-safe. I'll see if I can refactor my PR tomorrow.
@eviljoel can you try this branch and let me know how it goes for you?
As @keithrbennett pointed out, there is a problem in Processor#start
. The following minimal program can reproduce the error:
require 'semantic_logger'
SemanticLogger.clear_appenders!
SemanticLogger.add_appender(io: $stdout)
This results in the following backtrace:
Traceback (most recent call last):
2: from crash_test.rb:3:in `<main>'
1: from /home/eviljoel/.rvm/gems/ruby-2.7.6/gems/semantic_logger-4.12.0/lib/semantic_logger/semantic_logger.rb:169:in `add_appender'
/home/eviljoel/.rvm/gems/ruby-2.7.6/gems/semantic_logger-4.12.0/lib/semantic_logger/processor.rb:35:in `start': undefined local variable or method `thread' for #<SemanticLogger::Processor:0x00007fec9fe713a8> (NameError)
I'd like to wait for you to fix this issue before I update my branch. How I approach solving this issue is dependent upon how you fix this error.
@reidmorrison, just wanted to explicitly state that I'm waiting for clarity on all points raised in this comment before I continue to rebase his PR on your refactor-async
branch.
I am abandoning the changes in the refactor branch since they could break existing users of Semantic Logger and that is not a risk worth taking. I would suggest using a simpler logging implementation like the built in Ruby Logger to meet your unique use case.
Issue
None.
Changelog
Description of changes
When adding and clearing appenders from multiple threads (as well as performing similar operations like
SemanticLogger#close
), two problems can occur:This problem can be demonstrated with the following program:
After letting the program run for a minute or so, upon exit, an error like the following should occur:
While probably not strictly required, this MR builds off my prior PR (236). PR 236 is a bit more urgent as it corrects a threading error when the logger is managed in a single thread. This PR address additional thread safety issues when the logger is managed in multiple threads. I do not believe it makes much sense to merge this PR without the other one although an argument could be made to merge PR 236 without this one.
Admittedly, managing the logger in multiple threads is fairly unlikely, but these changes need to be made to ensure that "Semantic Logger is completely thread safe" and that "all methods can be called concurrently from any thread".
I tried to maintain compatibility with method signatures and behavior as much as possible. The big exception to this is 'Async#reopen'. I cannot implement reopen in a thread safe way and still work around the Ruby 2.5 Queue bug. This shouldn't be much of an issue as you really shouldn't be calling reopen unless you have forked. The work around I had to implement is to only have reopen do something if the appender thread is not running.
I removed the change log entry that reads "Fixes a race condition in
SemancicLogger.reopen
." as this PR completely clobbers znz's PR (223). I should probably point out that znz's change did not really address the race condition as&.
andkill
are not executed atomically.@thread
could still become nil between when&.
andkill
are executed. (I can't find any Ruby documentation that directly supports this claim, but the end of this article suggests@thread&.kill
is not executed atomically: https://lucaguidi.com/2014/03/27/thread-safety-with-ruby/)Thank you for your consideration. Please let me know if you have any questions.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.