mirror of https://github.com/iKarith/cppo-ng.git
108 lines
4.0 KiB
Python
108 lines
4.0 KiB
Python
# vim: set tabstop=4 shiftwidth=4 noexpandtab filetype=python:
|
|
|
|
# Copyright (C) 2017 T. Joseph Carter
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by the
|
|
# Free Software Foundation; either version 2 of the License, or (at your
|
|
# option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
# for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# 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 textwrap
|
|
import logging
|
|
# pylint: disable=unused-import
|
|
from logging import (
|
|
CRITICAL, DEBUG, ERROR, FATAL, INFO, WARNING,
|
|
Formatter,
|
|
StreamHandler
|
|
) # For export
|
|
# pylint: enable=unused-import
|
|
from typing import List, Dict
|
|
|
|
# pylint: disable=too-few-public-methods,missing-docstring
|
|
class Message(object):
|
|
def __init__(self, fmt: str, args: List) -> None:
|
|
self.fmt = fmt
|
|
self.args = args
|
|
|
|
def __str__(self) -> str:
|
|
return self.fmt.format(*self.args)
|
|
# pylint: enable=too-few-public-methods,missing-docstring
|
|
|
|
class StyleAdapter(logging.LoggerAdapter):
|
|
"""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: 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):
|
|
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('blocksfree'))
|