diff --git a/doc/devel/contributing.rst b/doc/devel/contributing.rst index 5140260b3f63..a90dc1694d6a 100644 --- a/doc/devel/contributing.rst +++ b/doc/devel/contributing.rst @@ -435,18 +435,22 @@ To include `logging` in your module, at the top of the module, you need to will log to a logger named ``matplotlib.yourmodulename``. If an end-user of Matplotlib sets up `logging` to display at levels -more verbose than `logger.WARNING` in their code as follows:: +more verbose than `logger.WARNING` in their code with the Matplotlib-provided +helper:: + + plt.set_loglevel("debug") + +or manually with :: import logging - fmt = '%(name)s:%(lineno)5d - %(levelname)s - %(message)s' - logging.basicConfig(level=logging.DEBUG, format=fmt) + logging.basicConfig(level=logging.DEBUG) import matplotlib.pyplot as plt Then they will receive messages like:: - matplotlib.backends: 89 - INFO - backend MacOSX version unknown - matplotlib.yourmodulename: 347 - INFO - Here is some information - matplotlib.yourmodulename: 348 - DEBUG - Here is some more detailed information + DEBUG:matplotlib.backends:backend MacOSX version unknown + DEBUG:matplotlib.yourmodulename:Here is some information + DEBUG:matplotlib.yourmodulename:Here is some more detailed information Which logging level to use? ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/faq/troubleshooting_faq.rst b/doc/faq/troubleshooting_faq.rst index 7002156948e2..3511a2141271 100644 --- a/doc/faq/troubleshooting_faq.rst +++ b/doc/faq/troubleshooting_faq.rst @@ -114,35 +114,18 @@ provide the following information in your e-mail to the `mailing list the error will help you find a bug in *your* code that is causing the problem. -* You can get helpful debugging output from Matlotlib by using the `logging` - library in your code and posting the verbose output to the lists. For a - command-line version of this, try:: +* Matplotlib provides debugging information through the `logging` library, and + a helper function to set the logging level: one can call :: - python -c "from logging import *; basicConfig(level=DEBUG); from pylab import *; plot(); show()" + plt.set_loglevel("info") # or "debug" for more info + to obtain this debugging information. - If you want to put the debugging hooks in your own code, then the - most simple way to do so is to insert the following *before* any calls - to ``import matplotlib``:: - - import logging - logging.basicConfig(level=logging.DEBUG) - - import matplotlib.pyplot as plt - - Note that if you want to use `logging` in your own code, but do not - want verbose Matplotlib output, you can set the logging level - for Matplotlib independently:: - - import logging - # set DEBUG for everything - logging.basicConfig(level=logging.DEBUG) - logger = logging.getLogger('matplotlib') - # set WARNING for Matplotlib - logger.setLevel(logging.WARNING) - - The `logging` module is very flexible, and can be a valuable tool in chasing - down errors. + Standard functions from the `logging` module are also applicable; e.g. one + could call ``logging.basicConfig(level="DEBUG")`` even before importing + Matplotlib (this is in particular necessary to get the logging info emitted + during Matplotlib's import), or attach a custom handler to the "matplotlib" + logger. This may be useful if you use a custom logging configuration. If you compiled Matplotlib yourself, please also provide: diff --git a/doc/users/next_whats_new/2019-01-24-AL-set_loglevel.rst b/doc/users/next_whats_new/2019-01-24-AL-set_loglevel.rst new file mode 100644 index 000000000000..9649ed80f481 --- /dev/null +++ b/doc/users/next_whats_new/2019-01-24-AL-set_loglevel.rst @@ -0,0 +1,7 @@ +:orphan: + +New logging API +``````````````` + +`matplotlib.set_loglevel`/`.pyplot.set_loglevel` can be called to display more +(or less) detailed logging output. diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 97c7fd5f05be..566cc14832aa 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -206,48 +206,43 @@ def _check_versions(): sys.argv = ['modpython'] -_verbose_msg = """\ -matplotlib.verbose is deprecated; -Command line argument --verbose-LEVEL is deprecated. -This functionality is now provided by the standard -python logging library. To get more (or less) logging output: - import logging - logger = logging.getLogger('matplotlib') - logger.set_level(logging.INFO)""" +# The decorator ensures this always returns the same handler (and it is only +# attached once). +@functools.lru_cache() +def _ensure_handler(): + """ + The first time this function is called, attach a `StreamHandler` using the + same format as `logging.basicConfig` to the Matplotlib root logger. + + Return this handler every time this function is called. + """ + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) + _log.addHandler(handler) + return handler -def _set_logger_verbose_level(level_str='silent', file_str='sys.stdout'): +def set_loglevel(level): """ - Use a --verbose-LEVEL level to set the logging level: + Sets the Matplotlib's root logger and root logger handler level, creating + the handler if it does not exist yet. + Typically, one should call ``set_loglevel("info")`` or + ``set_loglevel("debug")`` to get additional debugging information. + + Parameters + ---------- + level : {"notset", "debug", "info", "warning", "error", "critical"} + The log level of the handler. + + Notes + ----- + The first time this function is called, an additional handler is attached + to Matplotlib's root handler; this handler is reused every time and this + function simply manipulates the logger and handler's level. """ - levelmap = {'silent': logging.WARNING, 'helpful': logging.INFO, - 'debug': logging.DEBUG, 'debug-annoying': logging.DEBUG, - 'info': logging.INFO, 'warning': logging.WARNING} - # Check that current state of logger isn't already more verbose - # than the requested level. If it is more verbose, then leave more - # verbose. - newlev = levelmap[level_str] - oldlev = _log.getEffectiveLevel() - if newlev < oldlev: - _log.setLevel(newlev) - std = { - 'sys.stdout': sys.stdout, - 'sys.stderr': sys.stderr, - } - if file_str in std: - fileo = std[file_str] - else: - fileo = sys.stdout - try: - fileo = open(file_str, 'w') - # if this fails, we will just write to stdout - except IOError: - _log.warning('could not open log file "{0}" for writing. ' - 'Check your matplotlibrc'.format(file_str)) - console = logging.StreamHandler(fileo) - console.setLevel(newlev) - _log.addHandler(console) + _log.setLevel(level.upper()) + _ensure_handler().setLevel(level.upper()) def _logged_cached(fmt, func=None): diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 140aa451e0a2..52a2e1322d65 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -18,6 +18,7 @@ The object-oriented API is recommended for more complex plots. """ +import functools import importlib import inspect import logging @@ -166,6 +167,11 @@ def uninstall_repl_displayhook(): draw_all = _pylab_helpers.Gcf.draw_all +@functools.wraps(matplotlib.set_loglevel) +def set_loglevel(*args, **kwargs): # Ensure this appears in the pyplot docs. + return matplotlib.set_loglevel(*args, **kwargs) + + @docstring.copy(Artist.findobj) def findobj(o=None, match=None, include_self=True): if o is None: