-
Notifications
You must be signed in to change notification settings - Fork 119
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 etiquette #449
Comments
+1 from me, I find the current behaviour a) fiddly to deal with (as @coroa has beautifully outlined) and b) it encourages users to not learn about logging (yes logging hurts, but encouraging people to learn about it is probably a good thing)
I think this is a great workaround for those who really don't want to think about loggings. |
Ok, one more option, which could be a transitionary one: If we are in a notebook, we add a class JITConfigPseudoHandler(logging.Handler):
def __init__(self, remove_from_logger=None, **config_kwargs):
self.remove_from_logger = remove_from_logger
self.config_kwargs = config_kwargs
super().__init__()
def emit(self, record):
if self.remove_from_logger is not None:
self.remove_from_logger.removeHandler(self)
rootLogger = logging.getLogger()
if not rootLogger.hasHandlers():
logging.basicConfig(**self.config_kwargs)
logger.warning("Running logging.basicConfig for you, since you did not set up logging")
if <running_in_notebook>:
logger.addHandler(JITConfigPseudoHandler(logger, level="INFO", format='%(name)s - %(levelname)s: %(message)s')) Untested, but should work. Unsure whether the first record would have to be re-injected manually or would be shown correctly. |
Thanks @coroa & @znicholls for trying to make pyam more in line with best-practice python! I just looked at #294 and #288, where @znicholls refactored the logging implementation - but I have to admit that logging is still a bit of a mystery for me... Anyway, two comments: Less sophisticated solutionsMy experience with pyam development (and similar projects) has taught me some hesitancy to go for sophisticated solutions - because this makes maintenance really difficult when that one person is not available. An example is the unit-conversion-module - brilliant implementation, but only @khaeru can realistically make changes, so #442 depends on one person with limited time and a small stake in pyam. So I'm not sure if we even (still?) need to set up the logging and handlers in Make it easy for non-expertsOur user base largely consists of users with only basic python knowledge (that's the people who can benefit most from it), and I tried to write all tutorials in a way that they are helpful if this is the first time a user sees python code and can be a useful starting point for their own work. So statements like "encouraging people to learn logging" and "tell everyone to use ..." are really talking past our user base - hardly anyone ever reads the docs when something doesn't work as expected... RequirementsFrom my point of view, the only two requirements are that
|
I completely understand the comments and desire to make things easy. My suggestion would be that we try simply removing the null handler and logging initialisation as it currently exists and see what happens (@coroa nicely points out that things should still bubble up if we do this so we won't lose any messages). That should cover requirement 1. Reading https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided, I am concerned it will not hold for requirement 2 (and my quick experiment suggests requirement 2 doesn't work if we simply remove all the logging config). If requirement 2 is really important, I think we have a problem. In short, the requirements specified are what @coroa describes as 'rude'. I agree with this assessment. We cannot have a library which automatically sets up its own logging without it being annoying for anyone who wants to set up their logging in the standard ways. Further, having a library which sets up its own default logging level is in direct conflict with a library which isn't rude. (I think the note here https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library is important to respect.) I think @coroa's suggestion of a |
Playing a bit around and trying learn about logging - I think the following would be a simple solution without being too rude. # this is ignored if logging is already configured
logging.basicConfig(format='%(name)s - %(levelname)s: %(message)s')
logger = logging.getLogger(__name__)
# if logger is at default level WARNING, set to INFO
if logger.getEffectiveLevel() == 30:
logger.setLevel(20)
logger.info(f'Setting `{__name__}` logging level to `logging.INFO`') This approach respects any previous logging setup and does not make pyam loud if logging has been configured to a less verbose setting earlier. Checking for |
I don't agree with too coarse.
def basicConfig(**kwargs):
[...]
if len(root.handlers) == 0:
handlers = kwargs.pop("handlers", None)
[...] |
In my opinion this is worse as it requires people to setup their logging before they import pyam. It would be a nightmare working out where basicConfig is being called in a stack that had pyam as a dependency.
If a user tried to set the level as warning then this would update it to INFO (also annoying in my opinion). |
Hi folks, thought it could be useful to chime in here. First, love the discussion =) I think it's important to highlight that there are largely three 'user types' for pyam:
Notebooks are a first-class environment for pyam where we want to support fairly verbose logging statements to help guide these users, hence the original (rude) design of high-jacking the logging. However, it is fundamentally important to also provide first-class support for the other two user categories, and @coroa has provided a nice example of where we have failed to do so. Therefore, I would support a solution that raises our log level sufficiently if in a notebook environment, which I think @coroa has also provided. What do others think? |
Sounds good to me, as long as @danielhuppmann is now happy with @coroa's slightly more complicated than present solution |
@coroa's suggestion here would provide a hook that tests which environment the logger is in (notebook or not), and maintain current behavior if in a notebook. If I have understood it correctly =) @coroa - could you comment on whether we need a specific handler class as you suggest above or whether we could implement the logging functionality as-is but after checking the environment? |
Ok, then to sum everything up, I see now two options: 1a. KISSif <running_in_notebook>:
logging.basicConfig(level="INFO", format="....")
2a. With Handler from #449 (comment)Basically same solution as above, but defers running logging.basicConfig to when the first log message is generated.
Variant b: only affect loglevel of pyamIf you think, setting the global loglevel to INFO is too intrusive, I'd suggest to replace if not logger.root.hasHandlers():
logging.basicConfig(format="...")
logger.setLevel("INFO") I prefer to not touch the per-package loglevels, ie. leave them at My choiceWould be the slightly more sophisticated deferred running of |
Awesome summary thanks @coroa. I’d prefer 2a. As you say, super stable API with at least a few of us now who have looked at logging so maintenance should be fine. |
Thanks a lot, I agree that 2a seems like a really nice solution and striking a great balance between our different user groups! |
Fixes IAMconsortium#449. See also discussion in the issue.
Fixes IAMconsortium#449. See also discussion in the issue.
Fixes IAMconsortium#449. See also discussion in the issue.
Fixes IAMconsortium#449. See also discussion in the issue.
The current default logging behaviour by pyam is rude, if you actually want to configure logging.
Usually, one can expect a zen-like experience by just invoking
But pyam's initialization in
pyam/pyam/__init__.py
Line 14 in f071e79
and
pyam/pyam/__init__.py
Lines 21 to 26 in f071e79
does not respect these settings.
With those, one has to additionally issue
Annoyingly,
will still show
INFO
messages coming from pyam, since they are already written out into stderr by thepyam
logger.That's why the Configuring logging for a library section in the logging tutorial has this note:
I see two alternatives for improvement:
Best practice
Move the initialization into a separate routine
setup_logger
and tell everyone to use(
setup_logging
should take keyword arguments like level)If they forget about it, it's not that problematic either as long as we get in tandem rid of the
NullHandler
, because since python 3.2 the default behaviour of the logging library for aLogRecord
which bubbles through the logging tree without encountering a Handler is to send the message to stderr, if it is at least a warning (without timestamp and loglevel), as described in What happens if no configuration is provided.Looks like so:
Least change (but still slightly rude)
Get the root logger in
__init__.py
and check whether the user has set up any handlers before, as like:That way, the user experience for the average pyam user is unchanged, but one can configure logging, by using
as long as one does it before
import pyam
.And it is possible to make it shut up with
even after the import is through.
Thoughts? @danielhuppmann @gidden @znicholls .
I do strongly favor the Best practice option but would prepare a PR for either!
The text was updated successfully, but these errors were encountered: