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
logging in 3.7 behaves different due to caching #78450
Comments
When running the borgbackup tests on 3.7.0, quite some tests will be broken because expected log output is not emitted. I traced this down up the point when I noticed that reverting the caching feature change of isEnabledFor method in Logger makes the tests work on 3.7. I reviewed the logging code and the caching change, but could not easily find what is broken. There is of course some suspicion that cache invalidation might be the issue (it is present, but maybe it does not cover all the cases). See there for more info: So, I suggest to first revert the caching feature change so we get a fixed logging in 3.7.1 and then to retry to add caching. Note: I'm at EuroPython sprints today at the borgbackup table. |
Looks like this fix to our test tearDown method makes the tests work again. If one looks at what this dirty/problematic hack was done, it might be because of a missing logging reset functionality? |
I think I have found the bug. When we call setLevel then to clear the cache value of Logger objects cpython/Lib/logging/__init__.py Line 1265 in 9c18b1a
logging.Logger.manager.loggerDict.clear() before changing the level and hence it doesn't iterate through the dict to clear the logger object cache. This is not present in 3.6 since there is no Logger._cache and works fine.
Don't do logging.Logger.manager.loggerDict.clear() and you can see logging enabled for error as False because while doing setLevel we clear the cache of Logger objects of logging.Logger.manager.loggerDict.values() ➜ cpython git:(master) ✗ cat bpo-34269.py logger1 = logging.getLogger('abc')
logger2 = logging.getLogger('cde')
logger1.setLevel(logging.ERROR)
logger2.setLevel(logging.ERROR)
print("logging error : ", logger1.isEnabledFor(logging.ERROR))
print("logger dict : ", logging.Logger.manager.loggerDict)
print("Not clearing logging dict ")
# logging.Logger.manager.loggerDict.clear()
print("logger dict : ", logging.Logger.manager.loggerDict)
print("logging enabled for error : ", logger1.isEnabledFor(logging.ERROR))
if sys.version_info >= (3,7): # Print cache only for python 3.7 and above
# Since we clear logging.Logger.manager.loggerDict.clear() the cache is not reset
print("Cache ", logger1._cache)
print("Setting to critical")
logger1.setLevel(logging.CRITICAL)
if sys.version_info >= (3,7):
print("Cache after setting to critical ", logger1._cache)
print("logging enabled for error : ", logger1.isEnabledFor(logging.ERROR)) ➜ cpython git:(master) ✗ ./python.exe bpo-34269.py call logging.Logger.manager.loggerDict.clear() and you can see logging enabled for error as True because while doing setLevel we clear the cache of Logger objects of logging.Logger.manager.loggerDict.values() will be empty. ➜ cpython git:(master) ✗ cat bpo-34269.py logger1 = logging.getLogger('abc')
logger2 = logging.getLogger('cde')
logger1.setLevel(logging.ERROR)
logger2.setLevel(logging.ERROR)
print("logging error : ", logger1.isEnabledFor(logging.ERROR))
print("logger dict : ", logging.Logger.manager.loggerDict)
print("clearing logging dict ")
logging.Logger.manager.loggerDict.clear()
print("logger dict : ", logging.Logger.manager.loggerDict)
print("logging enabled for error : ", logger1.isEnabledFor(logging.ERROR))
if sys.version_info >= (3,7):
# Since we clear logging.Logger.manager.loggerDict.clear() the cache is not reset
print("Cache ", logger1._cache)
print("Setting to critical")
logger1.setLevel(logging.CRITICAL)
if sys.version_info >= (3,7):
print("Cache after setting to critical ", logger1._cache)
print("logging enabled for error : ", logger1.isEnabledFor(logging.ERROR)) ➜ cpython git:(master) ✗ ./python.exe bpo-34269.py Above script on Python 3.6 there is no Logger._cache and hence there is no cache invalidation to be done though logging.Logger.manager.loggerDict.clear() is called. ➜ cpython git:(master) ✗ python3.6 bpo-34269.py I have attached the test file that reproduces this. I think this is a bug. I don't know how to fix it since we can restrict the user from calling Thanks |
Well, loggerDict is an internal implementation detail which shouldn't be directly called by the code in borgbackup. Hence I'm not sure you can call it a bug. When messing around with internals of objects, caveats apply. Note that loggerDict isn't mentioned in the documentation: this is deliberate. |
Okay, I did some code search on GitHub for Thanks |
I agree that we should not dig that deep into logging internals and clear that dict from borg code. But, seems like one has no public api to reinitialize logging to a like-fresh state, right? So maybe THAT is the real problem. Add some .reset() method to do that? BTW, removing that .clear() from our code made our tests work again. I am not sure why that was added in the first place as I could not find regressions after removing it. |
For the use case of testing, you could use a context manager approach as described here in the logging cookbook: https://docs.python.org/3/howto/logging-cookbook.html#using-a-context-manager-for-selective-logging The approach outlined can be built on to add more convenience according to the specifics of your use case. In this way, you can restore logging to "the state in which you found it" which is a potentially more useful thing than just clearing out everything. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: