"""
Module Docstring
"""
__author__ = "Your Name"
__version__ = "0.1.0"
__license__ = "MIT"
from logger import setup_logger
logger = setup_logger(logfile=None)
def main():
""" Main entry point of the app """
logger.info("hello world")
if __name__ == "__main__":
""" This is executed when run from the command line """
main()
"""
This helper provides a versatile yet easy to use and beautiful
logging setup. You can use it to log to the console and optionally
to a logfile.
The formatter is heavily inspired by the Tornado web framework,
licensed under the Apache 2.0 license.
The call `logger.info("hello")` prints log messages in this format:
[I 170213 15:02:00 test:203] hello
Usage:
from logger import setup_logger
logger = setup_logger()
logger.info("message")
In order to also log to a file, just add a `logfile` parameter:
logger = setup_logger(logfile="/tmp/test.log")
The default loglevel is `logging.DEBUG`. You can set it with the
parameter `level`.
"""
import sys
import logging
try:
import curses
except ImportError:
curses = None
bytes_type = bytes
if sys.version_info >= (3, ):
unicode_type = str
basestring_type = str
xrange = range
else:
unicode_type = unicode
basestring_type = basestring
def setup_logger(name=__name__, logfile=None, level=logging.DEBUG):
"""
A utility function that you can call to easily set up logging to the
console and optionally to a file. No hassles.
"""
logger = logging.getLogger(name)
logger.propagate = False
logger.setLevel(level)
for handler in list(logger.handlers):
logger.removeHandler(handler)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(level)
formatter = LogFormatter()
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
if logfile:
filehandler = logging.FileHandler(logfile)
filehandler.setLevel(logging.NOTSET)
filehandler.setFormatter(formatter)
logger.addHandler(filehandler)
return logger
class LogFormatter(logging.Formatter):
"""
Log formatter used in Tornado. Key features of this formatter are:
* Color support when logging to a terminal that supports it.
* Timestamps on every log line.
* Robust against str/bytes encoding problems.
"""
DEFAULT_FORMAT = '%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s'
DEFAULT_DATE_FORMAT = '%y%m%d %H:%M:%S'
DEFAULT_COLORS = {
logging.DEBUG: 4,
logging.INFO: 2,
logging.WARNING: 3,
logging.ERROR: 1,
}
def __init__(self,
color=True,
fmt=DEFAULT_FORMAT,
datefmt=DEFAULT_DATE_FORMAT,
colors=DEFAULT_COLORS):
r"""
:arg bool color: Enables color support.
:arg string fmt: Log message format.
It will be applied to the attributes dict of log records. The
text between ``%(color)s`` and ``%(end_color)s`` will be colored
depending on the level if color support is on.
:arg dict colors: color mappings from logging level to terminal color
code
:arg string datefmt: Datetime format.
Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
.. versionchanged:: 3.2
Added ``fmt`` and ``datefmt`` arguments.
"""
logging.Formatter.__init__(self, datefmt=datefmt)
self._fmt = fmt
self._colors = {}
if color and _stderr_supports_color():
fg_color = (curses.tigetstr("setaf") or curses.tigetstr("setf") or
"")
if (3, 0) < sys.version_info < (3, 2, 3):
fg_color = unicode_type(fg_color, "ascii")
for levelno, code in colors.items():
self._colors[levelno] = unicode_type(
curses.tparm(fg_color, code), "ascii")
self._normal = unicode_type(curses.tigetstr("sgr0"), "ascii")
else:
self._normal = ''
def format(self, record):
try:
message = record.getMessage()
assert isinstance(message,
basestring_type)
record.message = _safe_unicode(message)
except Exception as e:
record.message = "Bad message (%r): %r" % (e, record.__dict__)
record.asctime = self.formatTime(record, self.datefmt)
if record.levelno in self._colors:
record.color = self._colors[record.levelno]
record.end_color = self._normal
else:
record.color = record.end_color = ''
formatted = self._fmt % record.__dict__
if record.exc_info:
if not record.exc_text:
record.exc_text = self.formatException(record.exc_info)
if record.exc_text:
lines = [formatted.rstrip()]
lines.extend(
_safe_unicode(ln) for ln in record.exc_text.split('\n'))
formatted = '\n'.join(lines)
return formatted.replace("\n", "\n ")
def _stderr_supports_color():
color = False
if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
try:
curses.setupterm()
if curses.tigetnum("colors") > 0:
color = True
except Exception:
pass
return color
_TO_UNICODE_TYPES = (unicode_type, type(None))
def to_unicode(value):
"""
Converts a string argument to a unicode string.
If the argument is already a unicode string or None, it is returned
unchanged. Otherwise it must be a byte string and is decoded as utf8.
"""
if isinstance(value, _TO_UNICODE_TYPES):
return value
if not isinstance(value, bytes):
raise TypeError(
"Expected bytes, unicode, or None; got %r" % type(value))
return value.decode("utf-8")
def _safe_unicode(s):
try:
return to_unicode(s)
except UnicodeDecodeError:
return repr(s)
if __name__ == "__main__":
logger = setup_logger()
logger.info("hello")