Skip to content
Open
66 changes: 66 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,71 @@
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
#dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

#Pycharm
.idea/*


.cache
.eggs
*.egg-info/
.*.swp
*.pyc

33 changes: 30 additions & 3 deletions coloredlogs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ def install(level=None, **kw):
:param fmt: Set the logging format (a string like those accepted by
:class:`~logging.Formatter`, defaults to
:data:`DEFAULT_LOG_FORMAT`).
:param overridefmt: A dictionary with custom level logging formats
:defaults to data:`DEFAULT_LOG_FORMAT`).
:param datefmt: Set the date/time format (a string, defaults to
:data:`DEFAULT_DATE_FORMAT`).
:param milliseconds: :data:`True` to show milliseconds like :mod:`logging`
Expand Down Expand Up @@ -396,7 +398,8 @@ def install(level=None, **kw):
handler.setLevel(level)
# Prepare the arguments to the formatter. The caller is
# allowed to customize `fmt' and/or `datefmt' as desired.
formatter_options = dict(fmt=kw.get('fmt'), datefmt=kw.get('datefmt'))
formatter_options = dict(fmt=kw.get('fmt'), datefmt=kw.get('datefmt'),
overridefmt=kw.get('overridefmt'))
# Come up with a default log format?
if not formatter_options['fmt']:
# Use the log format defined by the environment variable
Expand Down Expand Up @@ -447,7 +450,10 @@ def install(level=None, **kw):
if value is not None:
formatter_options[name] = value
# Create a (possibly colored) formatter.
if not use_colors:
formatter_options.pop('overridefmt', None)
formatter_type = ColoredFormatter if use_colors else logging.Formatter

handler.setFormatter(formatter_type(**formatter_options))
# Adjust the level of the selected logger.
adjust_level(logger, level)
Expand Down Expand Up @@ -809,7 +815,9 @@ class ColoredFormatter(logging.Formatter):
when you call :func:`coloredlogs.install()`.
"""

def __init__(self, fmt=None, datefmt=None, level_styles=None, field_styles=None):
formatters = {}

def __init__(self, fmt=None, datefmt=None, level_styles=None, field_styles=None, overridefmt=None):
"""
Initialize a :class:`ColoredFormatter` object.

Expand All @@ -830,12 +838,28 @@ def __init__(self, fmt=None, datefmt=None, level_styles=None, field_styles=None)
# that Sphinx doesn't embed the default values in the generated
# documentation (because the result is awkward to read).
fmt = fmt or DEFAULT_LOG_FORMAT

datefmt = datefmt or DEFAULT_DATE_FORMAT
# Initialize instance attributes.
self.level_styles = self.nn.normalize_keys(DEFAULT_LEVEL_STYLES if level_styles is None else level_styles)
self.field_styles = self.nn.normalize_keys(DEFAULT_FIELD_STYLES if field_styles is None else field_styles)
# Rewrite the format string to inject ANSI escape sequences and
# initialize the superclass with the rewritten format string.
if overridefmt is not None and isinstance(overridefmt, dict):

for level in ['INFO', 'WARNING', 'DEBUG',
'CRITICAL', 'ERROR', 'VERBOSE', 'FATAL']:
try:
if overridefmt.get(level) is not None:
_fmt = overridefmt[level].get('fmt', fmt)
_datefmt = overridefmt[level].get('datefmt', datefmt)
self.formatters[level] = logging.Formatter(
self.colorize_format(_fmt),
_datefmt
)
except Exception:
self.formatters.pop(level, None)

logging.Formatter.__init__(self, self.colorize_format(fmt), datefmt)

def colorize_format(self, fmt):
Expand Down Expand Up @@ -923,7 +947,10 @@ def format(self, record):
copy.msg = ansi_wrap(coerce_string(record.msg), **style)
record = copy
# Delegate the remaining formatting to the base formatter.
return logging.Formatter.format(self, record)
if self.formatters.get(record.levelname):
return self.formatters.get(record.levelname).format(record)
else:
return logging.Formatter.format(self, record)


if sys.version_info[:2] <= (2, 6):
Expand Down
5 changes: 4 additions & 1 deletion coloredlogs/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
# Modules included in our package.
from coloredlogs.converter import capture, convert
from coloredlogs.demo import demonstrate_colored_logging
from coloredlogs.demo import demonstrate_colored_logging_with_different_formatters

# Initialize a logger for this module.
logger = logging.getLogger(__name__)
Expand All @@ -60,7 +61,7 @@ def main():
try:
# Parse the command line arguments.
options, arguments = getopt.getopt(sys.argv[1:], 'cdh', [
'convert', 'to-html', 'demo', 'help',
'convert', 'to-html', 'demo', 'demo-with-formatter', 'help',
])
# Map command line options to actions.
for option, value in options:
Expand All @@ -69,6 +70,8 @@ def main():
arguments = []
elif option in ('-d', '--demo'):
actions.append(demonstrate_colored_logging)
elif option in ('-f', '--demo-with-formatter'):
actions.append(demonstrate_colored_logging_with_different_formatters)
elif option in ('-h', '--help'):
usage(__doc__)
return
Expand Down
57 changes: 57 additions & 0 deletions coloredlogs/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,60 @@ class RandomException(Exception):
logger.exception(e)
time.sleep(DEMO_DELAY)
logger.info("Done, exiting ..")


def demonstrate_colored_logging_with_different_formatters():
"""Interactively demonstrate the :mod:`coloredlogs` package with different formatters"""
# Initialize colored output to the terminal, default to the
# DEBUG logging level but enable the user the customize it.

FORMATS = {
"INFO": {'fmt': "%(asctime)s - %(levelname)s - "
"%(module)s - %(message)s"},
"DEBUG": {'fmt': "%(asctime)s - %(levelname)s - "
"%(module)s::%(funcName)s @ %(lineno)d - %(message)s"},
"WARNING": {'fmt': "%(asctime)s - %(message)s"}
# "WARNING": {}
}
FIELD_STYLES = dict(
asctime=dict(color='green'),
hostname=dict(color='magenta'),
levelname=dict(color='blue', bold=False),
programname=dict(color='cyan'),
name=dict(color='blue'),
module=dict(color='cyan'),
lineno=dict(color='magenta')
)

LEVEL_STYLES = {
'DEBUG': {"color": "blue"},
'INFO': {"color": "green"},
'WARNING': {"color": "yellow"},
'ERROR': {"color": "red"},
'CRITICAL': {"color": 'red'},
'FATAL': {"color": 'red'}
}

coloredlogs.install(level=os.environ.get('COLOREDLOGS_LOG_LEVEL', 'DEBUG'),
field_styles=FIELD_STYLES,
level_styles=LEVEL_STYLES,
overridefmt=FORMATS)
# Print some examples with different timestamps.
for level in ['spam', 'debug', 'verbose', 'info', 'notice', 'warn',
'error', 'critical']:
if hasattr(logger, level):
getattr(logger, level)("message with level %r", level)
time.sleep(DEMO_DELAY)
# Show how exceptions are logged.
try:
class RandomException(Exception):
pass
raise RandomException("Something went horribly wrong!")
except Exception as e:
logger.exception(e)
time.sleep(DEMO_DELAY)
logger.info("Done, exiting ..")


demonstrate_colored_logging()
demonstrate_colored_logging_with_different_formatters()
9 changes: 9 additions & 0 deletions coloredlogs/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@ def test_cli_demo(self):
for name in 'debug', 'info', 'warning', 'error', 'critical':
assert name.upper() in output

def test_cli_demo_with_formatters(self):
"""Test the command line colored logging demonstration."""
with CaptureOutput() as capturer:
main('coloredlogs', '--demo-with-formatter')
output = capturer.get_text()
# Make sure the output contains all of the expected logging level names.
for name in 'debug', 'info', 'error', 'critical':
assert name.upper() in output

def test_cli_conversion(self):
"""Test the command line HTML conversion."""
output = main('coloredlogs', '--convert', 'coloredlogs', '--demo', capture=True)
Expand Down
Binary file added dist/coloredlogs-5.0.1.internal.tar.gz
Binary file not shown.