tested on 0.14.0
import logging
import sys
import salt.client
logger = logging.getLogger()
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format="Debug: %(message)s")
logger.debug("Test")
Nothing show, but if I move salt.client
import just after the logging is initialized:
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format="Debug: %(message)s")
import salt.client
logger = logging.getLogger()
logger.debug("Test")
I get:
Debug: Test
You know how a building is imploded?
It's a mess right?
A controlled mess.
You might think salt's logging is also a mess... at most, a controlled one.
Salt does try to make python's logging work for it.
We try to overcome some common obstacles.
I'll explain what exactly has happened with the above code.
When salt.log
get's imported(by importing salt.client
), it installs a Null logging handler, if, there's no logging handler already configured(your 2nd example).
This is required so we don't get errors like, no handler for _foo_, which happens when we try to log too soon.
There's some more _black_ magic with salt's logging, so, the real question is, what exactly are you trying to achieve in which salt's logging is getting in your way?
If you're trying to integrate salt in your own library, either setup your own logging before importing salt, or, as suggested here, import it really soon, then use salt.log.setup_console_logger
and/or salt.log.setup_logfile_logger
to configure it, also, as soon as possible.
I didn't said here that salt logging is a mess.
I was considering salt.client
as a library that can be imported and use like any other. I never had this issue before with an other Python library, in stdlib or not.
check here:
http://docs.python.org/2/howto/logging.html#configuring-logging-for-a-library
Unless salt.client
isn't a library.
Usually, loggers are initialized by the consumer of those libraries, such as salt-master
, salt-minion
, salt-run
or salt-call
. Or in my case, my own code.
I never had to configure loggers before import anything from boto, django or stdlib that is in their API.
So, if I write a python library that import salt.client
, I always have to initialize logging _before_ import my own library?
Logging to console or a log file, aren't the only 2 usage of python logging library. In my case, I wrote some unittest that execute salt state to test them and I wanted to report error to sentry (using raven logger) and send email to me directly if one of my logger log an error.
I would pay in blood to have logging configurable to support the zeromq appender for logstash: https://github.com/mattmcclean/logstash-python
As much as I appreciate your enthusiasm, the currency of the realm, is currency :)
Well, in this case, a library shouldn't initialize logging, as said here: http://docs.python.org/2/howto/logging.html#configuring-logging-for-a-library
but to get a logstash handler (and for me to get a raven handler [to receive logs with error level in sentry] and graypy handler [to get logs in graylog2]), the minion, master, syndic, salt-call, salt-run, etc should all initialize logging with logging.config.dictConfig and the dict defined in /etc/salt/$config
.
Let's clarify this issue.
As suggested by the logging library, the link you posted @bclermont, and I quote:
If the using application does not configure logging, and library code makes logging calls, then
(as described in the previous section) an error message will be printed to sys.stderr.
If for some reason you don鈥檛 want this message printed in the absence of any logging configuration,
you can attach a do-nothing handler to the top-level logger for your library. This avoids the message
being printed, since a handler will be always be found for the library鈥檚 events: it just doesn鈥檛 produce
any output.
That said, as soon as you import any salt module, we import salt.log
(this line).
The repercussion of that import is that:
sys.stderr
stream handler here, so we can later remove it, and only our own handler>>> import logging
>>> logging.getLoggerClass()
<class 'logging.Logger'>
>>> logging.root.handlers
[]
>>> import salt.client
>>> logging.root.handlers
[<logging.NullHandler object at 0xb71ff1ec>]
>>> logging.getLoggerClass()
<class 'salt.log.SaltLoggingClass'>
>>>
So, although we're doin' some more stuff, like setting the default logging class, adding the extra levels, and setting the root logger level to garbage, we're actually following the python's logging documentation regarding the null handler.
No other logging configuration and/or tweak is done. The only thing I could find(on a fast search) is this line of code which set's up the temporary logging handler but is only triggered if msgpack is not installed.
Other than that, any further logging configuration is only done on CLI access code like salt.utils.parsers
.
Regarding dictConfig
, we've already discussed this here @bclermont. Although the formatting of my reply is not the best here in GitHub, it reads better in the email, I'll remember the conclusion:
I basically said, I'm not going to do it(at least, not right now), not because you're wrong,
but because the need for that much power was asked by a single user, you.
So, since it's only you, since there's no "massive" request for it, since it's more error
prone for the overall salt users, and since salt itself, currently, does not require those
changes, we accept pull requests. You're free to add that support to salt, we will be
more than glad to review your pull request and include it in salt's code.
It still applies. A LOT more has fallen in my hands, it's still not on the top of my priority list. But we _still_ accept pull requests :smile:
I have some free time this weekend. Where would I look in the code-base to add dictConfig
support? Also, why dictConfig
over fileConfig
?
dictConfig because dictionaries can be pulled from salt's yaml config.
fileConfig could also be used and have it's path configured in salt config.
I think it's a matter of preference.
Regarding where, logging config should go in salt.log, cli config should go in salt.utils.parsers or salt.config if possible or salt.cli, its a matter of what needs to be done.
I had an issue with this as well. The workaround (instantiating the logging object right before importing salt modules) worked for me. Without that I wasn't getting any output from my flask application.
@stephendotexe I'm late on creating a document which explains that procedure and other particularities about salt logging. Sorry.
+1
It took me some time to figure this out.
Should it rather be a bug than a feature?
#!/usr/bin/env python
import logging
# Without this line `salt.client` somehow prevents all subsequent `logging` output.
logging.debug('initialize logging')
# Salt modules.
import salt.client
# ... other imports
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.
This is still a bug and still present as far as I can tell. At the very least this needs to be documented on the salt client page if it's not going to be changing, because it's not expected behavior in most non-salt python modules.
Nonworking Example:
import logging
import salt.client
print('before logger')
logging.basicConfig(
format='%(asctime)s %(levelname)s:%(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p',
level=logging.INFO)
logging.info('info')
logging.warning('warning')
logging.error('error')
print('after logger')
Output:
$ python example.py
before logger
after logger
Working Example:
import logging
print('before logger')
logging.basicConfig(
format='%(asctime)s %(levelname)s:%(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p',
level=logging.INFO)
import salt.client
logging.info('info')
logging.warning('warning')
logging.error('error')
print('after logger')
Output:
$ python example.py
before logger
01/29/2020 09:40:32 AM INFO:info
01/29/2020 09:40:32 AM WARNING:warning
01/29/2020 09:40:32 AM ERROR:error
after logger
Care to open a new issue requesting a documentation clarification please?
Salt is not a library, though it can be used as one, and the working example you posted is how you can use it as a library with regards to the expected logging behavior of libraries.
Most helpful comment
+1
It took me some time to figure this out.
Should it rather be a bug than a feature?