-
Couldn't load subscription status.
- Fork 5.6k
Fix to ensure that asyncio uses SaltLoggingClass #68401
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
base: 3007.x
Are you sure you want to change the base?
Conversation
da61268 to
f1b3d17
Compare
Fixes the salt logging implementation to check asyncio is using the SaltLoggingClass if it has already been imported. If it uses the standard logging.Logger() class then extra fields in the log format such as %(jid)s will cause an exception when asyncio logs anything.
f1b3d17 to
29b7198
Compare
| if "asyncio" in sys.modules: | ||
| asyncio_logger = logging.getLogger("asyncio") | ||
| if not isinstance(asyncio_logger, SaltLoggingClass): | ||
| asyncio_logger.__class__ = SaltLoggingClass |
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.
Is this really the correct/ideal way to do this? Shouldn't a the logger be created/instantiated as a SaltLoggingClass and assigned to the logger instead of mucking with the internal attributes?
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.
The logger for asyncio isn't being created in salt - it's created within asyncio and happens as soon as you import it:
>>> import logging
>>> logging.root.getChildren()
set()
>>> import asyncio
>>> logging.root.getChildren()
{<Logger asyncio (WARNING)>}
>>>As I noted above, the other option is to try to make sure that asyncio is imported after salt._logging, but that is somewhat fragile and it also doesn't work in the test suite where asyncio gets imported by pytest (or maybe one of the pytest extensions..)
I don't love it as a solution, but I can't see a better way to do it - open to suggestions if you can.
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.
Would something like this quick untested rough draft (with the help from llm) work?
import logging
def ensure_asyncio_uses_salt_logger(SaltLoggingClass):
name = "asyncio"
mgr = logging.root.manager
# Preserve any existing logger settings
existing = mgr.loggerDict.get(name)
handlers = getattr(existing, "handlers", []) if isinstance(existing, logging.Logger) else []
level = getattr(existing, "level", logging.NOTSET) if isinstance(existing, logging.Logger) else logging.NOTSET
propagate = getattr(existing, "propagate", True) if isinstance(existing, logging.Logger) else True
# Create SaltLoggingClass logger instance
salt_logger = SaltLoggingClass(name)
# Copy preserved state
salt_logger.handlers[:] = handlers[:] # shallow copy handlers list
salt_logger.setLevel(level)
salt_logger.propagate = propagate
# Replace manager entry so logging.getLogger(name) returns salt_logger
mgr.loggerDict[name] = salt_logger
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.
Thanks for the suggestion, although that won't entirely solve the problem. Whilst that will replace the logger for asyncio in the logging manager and any new calls to logging.getLogger("asyncio") will return the new logger, the logger used by the asyncio module has already been assigned, so it will continue to use the old logger object.
We could do something like this and then update asyncio.logger I guess. That said, I'm wondering if it makes more sense to move the code that enriches the log records (ie adding jid) out of the SaltLoggerClass and into a filter that can be applied to existing logging classes. Will have a play about with that to see how much effort that'd be.
What does this PR do?
Adds a check in to
salt._logging.implto ensure that the logger forasynciois usingSaltLoggingClass. Ifasynciohas been imported beforesalt._loggingthen the class is already set tologging.Logger. That can't handle additional fields such as%(jid)sand will throw an exception if that is set in the fmt string.I considered just changing the order of imports in
salt/__init__.py, which works when running salt normally, however it was impossible to test as the test suite always importsasynciowell before anysaltmodules are imported.What issues does this PR fix or reference?
Fixes #68400
Previous Behavior
When using a
log_fmt_consolewith%(jid)sand alog_levelofdebug, there would be exceptions like this:New Behavior
No exceptions are thrown
Merge requirements satisfied?
[NOTICE] Bug fixes or features added to Salt require tests.
Commits signed with SSH?
Yes