mirror of https://github.com/iKarith/cppo-ng.git
Mostly style improvements to logging.py
Our changes to the built-in logging module of Python are kind of a hack designed to be as lightweight as possible way to replace the built-in logging module with one that operates using newer str.format based string expansion. It's not really complete we probably should change that at some point. Changes include: - Docstrings, lots of docstrings - Type hinting - log is now LOG - pylint warnings disabled on things that will not change and are on purpose - StyleAdapter.log does not dedent msg anymore unless dedent=True is passed which hopefully should make it a little less DWIM.
This commit is contained in:
parent
db6c481ad6
commit
8212c2f848
|
@ -16,37 +16,95 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""BlocksFree LoggingAdapter using str.format and textwrap.dedent
|
||||
|
||||
Traditionally Python has used a % operator on strings to perform a somewhat
|
||||
printf-like string formatting operation. It has limitations and str.format was
|
||||
made to replace it. The old way isn't deprecated yet, and it won't be removed
|
||||
any time soon, but str.format is how new code should work.
|
||||
|
||||
The issue is that Python's logging module is used by old and new code alike,
|
||||
and so calls to the basic logger require the use of %-style format strings in
|
||||
order to work with new and old code alike, forcing you to use the less flexible
|
||||
"old way".
|
||||
|
||||
There isn't a perfectly clean solution to this--either you need ugliness in
|
||||
each logging call, or you need a bit different ugliness up front to hide the
|
||||
mess.
|
||||
|
||||
Details are at the bottom of this section of the Logging Cookbook:
|
||||
|
||||
https://docs.python.org/howto/logging-cookbook.html\
|
||||
#use-of-alternative-formatting-styles
|
||||
|
||||
The next issue was that multi-line strings either break code indentation flow,
|
||||
or they have to deal with indentation. If you don't have a special case for
|
||||
the first line, textwrap.dedent works great for that. In order to avoid the
|
||||
special case, just use a line continuation immediately after your opening
|
||||
quotes. Another imperfect solution, but it does the job.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import textwrap
|
||||
from typing import List, Dict
|
||||
|
||||
### LOGGING
|
||||
# *sigh* No clean/simple way to use str.format() type log strings without
|
||||
# jumping through a few hoops
|
||||
|
||||
# pylint: disable=too-few-public-methods,missing-docstring
|
||||
class Message(object):
|
||||
def __init__(self, fmt, args):
|
||||
def __init__(self, fmt: str, args: List) -> None:
|
||||
self.fmt = fmt
|
||||
self.args = args
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.fmt.format(*self.args)
|
||||
# pylint: enable=too-few-public-methods,missing-docstring
|
||||
|
||||
class StyleAdapter(logging.LoggerAdapter):
|
||||
def __init__(self, logger, extra=None):
|
||||
"""Return a LoggerAdapter that uses str.format expansions in log messages
|
||||
|
||||
StyleAdapter wraps the standard logger (e.g. logging.getLogger) so that it
|
||||
appears to take str.format strings rather than the classic str % tuple
|
||||
strings.
|
||||
|
||||
Args:
|
||||
logger: A logging.Logger instance/logging channel
|
||||
extra: A context object (see the Python logging cookbook)
|
||||
"""
|
||||
|
||||
def __init__(self, logger: logging.Logger, extra=None) -> None:
|
||||
super(StyleAdapter, self).__init__(logger, extra or {})
|
||||
|
||||
def log(self, level, msg, *args, **kwargs):
|
||||
def log(self, level: int, msg: str, *args: List, **kwargs: Dict) -> None:
|
||||
"""Logs msg.format(*args) at the given level
|
||||
|
||||
Effectively functions as if we were subclassing logging.Logger's log
|
||||
method to change arg convention from msg % args to msg.format(*args).
|
||||
See the documentation for the standard logging module for more info.
|
||||
|
||||
Additionally can perform textwrap.dedent() on msg before logging if
|
||||
dedent=True.
|
||||
|
||||
Args:
|
||||
level: Integer logging level
|
||||
msg: A log message in the form of a str.format format string
|
||||
dedent: Whether to dedent format string
|
||||
args/kwargs: Positional and keyword arguments passed to _log
|
||||
"""
|
||||
if self.isEnabledFor(level):
|
||||
msg, kwargs = self.process(textwrap.dedent(msg), kwargs)
|
||||
if kwargs.get('dedent', False):
|
||||
msg = textwrap.dedent(msg)
|
||||
msg, kwargs = self.process(msg, kwargs)
|
||||
# pylint: disable=protected-access
|
||||
self.logger._log(level, Message(str(msg), args), (), **kwargs)
|
||||
# pylint: enable=protected-access
|
||||
|
||||
log = StyleAdapter(logging.getLogger(__name__))
|
||||
|
||||
LOG = StyleAdapter(logging.getLogger(__name__))
|
||||
|
||||
# Set up our logging facility
|
||||
_handler = logging.StreamHandler(sys.stdout)
|
||||
_formatter = logging.Formatter('%(message)s')
|
||||
_handler.setFormatter(_formatter)
|
||||
log.logger.addHandler(_handler)
|
||||
log.setLevel(logging.DEBUG)
|
||||
# FIXME(tjcarter): get rid of log, let caller handle where it's going
|
||||
log = LOG
|
||||
_HANDLER = logging.StreamHandler(sys.stdout)
|
||||
_FORMATTER = logging.Formatter('{message}', style='{')
|
||||
_HANDLER.setFormatter(_FORMATTER)
|
||||
LOG.logger.addHandler(_HANDLER)
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
|
|
Loading…
Reference in New Issue