Skip to content

Option to pass separate log formats for different log levels #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.