coloredlogs: Colored terminal output for Python’s logging module

The coloredlogs package enables colored terminal output for Python’s logging module. The ColoredFormatter class inherits from logging.Formatter and uses ANSI escape sequences to render your logging messages in color. It uses only standard colors so it should work on any UNIX terminal. It’s currently tested on Python 2.6, 2.7, 3.4, 3.5 and PyPy. On Windows coloredlogs automatically pulls in Colorama as a dependency and enables ANSI escape sequence translation using Colorama. Here is a screen shot of the demo that is printed when the command coloredlogs --demo is executed:

Note that the screenshot above includes the custom logging level VERBOSE defined by my verboselogs package: if you install both coloredlogs and verboselogs it will Just Work (verboselogs is of course not required to use coloredlogs).

Format of log messages

The ColoredFormatter class supports user defined log formats so you can use any log format you like. The default log format is as follows:

%(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s

This log format results in the following output:

2015-10-23 03:32:22 peter-macbook coloredlogs.demo[30462] DEBUG message with level 'debug'
2015-10-23 03:32:23 peter-macbook coloredlogs.demo[30462] VERBOSE message with level 'verbose'
2015-10-23 03:32:24 peter-macbook coloredlogs.demo[30462] INFO message with level 'info'

You can customize the log format and styling using environment variables as well as programmatically, please refer to the online documentation for details.


Here’s an example of how easy it is to get started:

import coloredlogs, logging

# Create a logger object.
logger = logging.getLogger('your-module')

# By default the install() function installs a handler on the root logger,
# this means that log messages from your code and log messages from the
# libraries that you use will all show up on the terminal.

# If you don't want to see log messages from libraries, you can pass a
# specific logger object to the install() function. In this case only log
# messages originating from that logger will show up on the terminal.
coloredlogs.install(level='DEBUG', logger=logger)

# Some examples.
logger.debug("this is a debugging message")"this is an informational message")
logger.warn("this is a warning message")
logger.error("this is an error message")
logger.critical("this is a critical message")

Colored output from cron

When coloredlogs is used in a cron job, the output that’s e-mailed to you by cron won’t contain any ANSI escape sequences because coloredlogs realizes that it’s not attached to an interactive terminal. If you’d like to have colors e-mailed to you by cron there’s a simple way to set it up:

* * * * * root coloredlogs --to-html your-command

The coloredlogs program is installed when you install the coloredlogs package. When you execute coloredlogs --to-html your-command it runs your-command under the external program script (you need to have this installed). This makes your-command think that it’s attached to an interactive terminal which means it will output ANSI escape sequences which will then be converted to HTML by the coloredlogs program. Yes, this is a bit convoluted, but it works great :-)

You can use this feature without using coloredlogs in your Python modules, but please note that only normal text, bold text and text with one of the foreground colors black, red, green, yellow, blue, magenta, cyan and white (these are the portable ANSI color codes) are supported.


The latest version of coloredlogs is available on PyPI and GitHub. The online documentation is available on Read The Docs. For bug reports please create an issue on GitHub. If you have questions, suggestions, etc. feel free to send me an e-mail at


This software is licensed under the MIT license.

© 2017 Peter Odding.

API documentation

The following documentation is based on the source code of version 6.0 of the coloredlogs package.

The most useful entry points into the documentation are install(), ColoredFormatter and enable_system_logging().

The coloredlogs module

Colored terminal output for Python’s logging module.

Getting started

The easiest way to get started is by importing coloredlogs and calling coloredlogs.install() (similar to logging.basicConfig()):

>>> import coloredlogs, logging
>>> coloredlogs.install(level='DEBUG')
>>> logger = logging.getLogger('')
>>>"this is an informational message")
2015-10-22 19:13:52 peter-macbook[28036] INFO this is an informational message

The install() function creates a ColoredFormatter that injects ANSI escape sequences into the log output.

Environment variables

The following environment variables can be used to configure the coloredlogs module without writing any code:

Environment variable Default value Type of value
$COLOREDLOGS_AUTO_INSTALL ‘false’ a boolean that controls whether auto_install() is called
$COLOREDLOGS_LOG_LEVEL ‘INFO’ a log level name

Examples of customization

Here we’ll take a look at some examples of how you can customize coloredlogs using environment variables.

Changing the log format

The simplest customization is to change the log format, for example:

$ export COLOREDLOGS_LOG_FORMAT='[%(hostname)s] %(asctime)s - %(message)s'
$ coloredlogs --demo
[peter-macbook] 2015-10-22 23:42:28 - message with level 'debug'
[peter-macbook] 2015-10-22 23:42:29 - message with level 'verbose'

Here’s what that looks like in a terminal (I always work in terminals with a black background and white text):

Screen shot of colored logging with custom log format.

Changing the date/time format

You can also change the date/time format, for example you can remove the date part and leave only the time:

$ export COLOREDLOGS_LOG_FORMAT='%(asctime)s - %(message)s'
$ coloredlogs --demo
23:45:22 - message with level 'debug'
23:45:23 - message with level 'verbose'

Here’s what it looks like in a terminal:

Screen shot of colored logging with custom date/time format.

Changing the colors/styles

Finally you can customize the colors and text styles that are used:

$ export COLOREDLOGS_LOG_FORMAT='%(asctime)s - %(message)s'
$ export COLOREDLOGS_FIELD_STYLES='' # no styles
$ export COLOREDLOGS_LEVEL_STYLES='warning=yellow;error=red;critical=red,bold'
$ coloredlogs --demo
23:45:22 - message with level 'debug'
23:45:23 - message with level 'verbose'

The difference isn’t apparent from the above text but take a look at the following screen shot:

Screen shot of colored logging with custom colors.

Some notes about log levels

With regards to the handling of log levels, the coloredlogs package differs from Python’s logging module in two aspects:

  1. While the logging module uses the default logging level logging.WARNING, the coloredlogs package has always used logging.INFO as its default log level.

  2. When logging to the terminal or system log is initialized by install() or enable_system_logging() the effective level [1] of the selected logger [2] is compared against the requested level [3] and if the effective level is more restrictive than the requested level, the logger’s level will be set to the requested level. The reason for this is to work around a combination of design choices in Python’s logging module that can easily confuse people who aren’t already intimately familiar with it:

    • All loggers are initialized with the level logging.NOTSET.
    • When a logger’s level is set to logging.NOTSET the getEffectiveLevel() method will fall back to the level of the parent logger.
    • The parent of all loggers is the root logger and the root logger has its level set to logging.WARNING by default (after importing the logging module).

    Effectively all user defined loggers inherit the default log level logging.WARNING from the root logger, which isn’t very intuitive for those who aren’t already familiar with the hierarchical nature of the logging module.

    By avoiding this potentially confusing behavior (see #14, #18, #21, #23 and #24), while at the same time allowing the caller to specify a logger object, my goal and hope is to provide sane defaults that can easily be changed when the need arises.


    Refer to logging.Logger.getEffectiveLevel() for details.


    The logger that is passed as an argument by the caller or the root logger which is selected as a default when no logger is provided.


    The log level that is passed as an argument by the caller or the default log level logging.INFO when no level is provided.

Classes and functions

coloredlogs.DEFAULT_LOG_LEVEL = 20

The default log level for coloredlogs (logging.INFO).

coloredlogs.DEFAULT_LOG_FORMAT = '%(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s'

The default log format for ColoredFormatter objects (a string).

coloredlogs.DEFAULT_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'

The default date/time format for ColoredFormatter objects (a string).

coloredlogs.CHROOT_FILES = ['/etc/debian_chroot']

A list of filenames that indicate a chroot and contain the name of the chroot.

coloredlogs.CAN_USE_BOLD_FONT = True

Whether bold fonts can be used in default styles (a boolean).

This is disabled on Windows because in my (admittedly limited) experience the ANSI escape sequence for bold font is simply not translated by Colorama, instead it’s printed to the terminal without any translation.

coloredlogs.DEFAULT_FIELD_STYLES = {'hostname': {'color': 'magenta'}, 'programname': {'color': 'cyan'}, 'name': {'color': 'blue'}, 'levelname': {'color': 'black', 'bold': True}, 'asctime': {'color': 'green'}}

Mapping of log format names to default font styles.

coloredlogs.DEFAULT_LEVEL_STYLES = {'info': {}, 'notice': {'color': 'magenta'}, 'verbose': {'color': 'blue'}, 'spam': {'color': 'green'}, 'critical': {'color': 'red', 'bold': True}, 'error': {'color': 'red'}, 'debug': {'color': 'green'}, 'warning': {'color': 'yellow'}}

Mapping of log level names to default font styles.


Automatically call install() when $COLOREDLOGS_AUTO_INSTALL is set.

The coloredlogs package includes a path configuration file that automatically imports the coloredlogs module and calls auto_install() when the environment variable $COLOREDLOGS_AUTO_INSTALL is set.

This function uses coerce_boolean() to check whether the value of $COLOREDLOGS_AUTO_INSTALL should be considered True.

coloredlogs.install(level=None, **kw)[source]

Enable colored terminal output for Python’s logging module.


The coloredlogs.install() function is similar to logging.basicConfig(), both functions take a lot of optional keyword arguments but try to do the right thing by default:

  1. If reconfigure is True (it is by default) and an existing StreamHandler is found that is connected to either stdout or stderr the handler will be removed. This means that first calling logging.basicConfig() and then calling coloredlogs.install() will replace the stream handler instead of adding a duplicate stream handler. If reconfigure is False and an existing handler is found no further steps are taken (to avoid installing a duplicate stream handler).
  2. A StreamHandler is created and connected to the stream given by the stream keyword argument (sys.stderr by default). The stream handler’s level is set to the value of the level keyword argument.
  3. A ColoredFormatter is created if the isatty keyword argument allows it (or auto-detection allows it), otherwise a normal Formatter is created. The formatter is initialized with the fmt and datefmt keyword arguments (or their computed defaults).
  4. HostNameFilter.install() and ProgramNameFilter.install() are called to enable the use of additional fields in the log format.
  5. If the logger’s level is too restrictive it is relaxed (refer to notes about log levels for details).
  6. The formatter is added to the handler and the handler is added to the logger.

Increase the verbosity of the root handler by one defined level.

Understands custom logging levels like defined by my verboselogs module.


Decrease the verbosity of the root handler by one defined level.

Understands custom logging levels like defined by my verboselogs module.


Check whether the log level of the root handler is set to a verbose level.

Returns:True if the root handler is verbose, False if not.

Get the logging level of the root handler.

Returns:The logging level of the root handler (an integer) or DEFAULT_LOG_LEVEL (if no root handler exists).

Set the logging level of the root handler.

Parameters:level – The logging level to filter on (an integer or string).

If no root handler exists yet this automatically calls install().


Find the defined logging levels.

Returns:A dictionary with level names as keys and integers as values.

Here’s what the result looks like by default (when no custom levels or level names have been defined):

>>> find_defined_levels()
{'NOTSET': 0,
 'DEBUG': 10,
 'INFO': 20,
 'WARN': 30,
 'WARNING': 30,
 'ERROR': 40,
 'FATAL': 50,
 'CRITICAL': 50}

Coerce a logging level name to a number.

Parameters:value – A logging level (integer or string).
Returns:The number of the log level (an integer).

This function translates log level names into their numeric values. The logging module does this for us on Python 2.7 and 3.4 but fails to do so on Python 2.6 which coloredlogs still supports.


Find log level names which are aliases of each other.

Returns:A dictionary that maps aliases to their canonical name.


Canonical names are chosen to be the alias with the longest string length so that e.g. WARN is an alias for WARNING instead of the other way around.

Here’s what the result looks like by default (when no custom levels or level names have been defined):

>>> from coloredlogs import find_level_aliases
>>> find_level_aliases()
coloredlogs.parse_encoded_styles(text, normalize_key=None)[source]

Parse text styles encoded in a string into a nested data structure.

Parameters:text – The encoded styles (a string).
Returns:A dictionary in the structure of the DEFAULT_FIELD_STYLES and DEFAULT_LEVEL_STYLES dictionaries.

Here’s an example of how this function works:

>>> from coloredlogs import parse_encoded_styles
>>> from pprint import pprint
>>> encoded_styles = 'debug=green;warning=yellow;error=red;critical=red,bold'
>>> pprint(parse_encoded_styles(encoded_styles))
{'debug': {'color': 'green'},
 'warning': {'color': 'yellow'},
 'error': {'color': 'red'},
 'critical': {'bold': True, 'color': 'red'}}

Find the host name to include in log messages.

Parameters:use_chroot – Use the name of the chroot when inside a chroot? (boolean, defaults to True)
Returns:A suitable host name (a string).

Looks for CHROOT_FILES that have a nonempty first line (taken to be the chroot name). If none are found then socket.gethostname() is used as a fall back.


Select a suitable program name to embed in log messages.

Returns:One of the following strings (in decreasing order of preference):
  1. The base name of the currently running Python program or script (based on the value at index zero of sys.argv).
  2. The base name of the Python executable (based on sys.executable).
  3. The string ‘python’.
coloredlogs.replace_handler(logger, match_handler, reconfigure)[source]

Prepare to replace a handler.


A tuple of two values:

  1. The matched Handler object or None if no handler was matched.
  2. The Logger to which the matched handler was attached or the logger given to replace_handler().

coloredlogs.find_handler(logger, match_handler)[source]

Find a (specific type of) handler in the propagation tree of a logger.

  • logger – The logger to check (a Logger object).
  • match_handler – A callable that receives a Handler object and returns True to match a handler or False to skip that handler and continue searching for a match.

A tuple of two values:

  1. The matched Handler object or None if no handler was matched.
  2. The Logger object to which the handler is attached or None if no handler was matched.

This function finds a logging handler (of the given type) attached to a logger or one of its parents (see walk_propagation_tree()). It uses the undocumented handlers attribute to find handlers attached to a logger, however it won’t raise an exception if the attribute isn’t available. The advantages of this approach are:

  • This works regardless of whether coloredlogs attached the handler or other Python code attached the handler.
  • This will correctly recognize the situation where the given logger has no handlers but propagate is enabled and the logger has a parent logger that does have a handler attached.
coloredlogs.match_stream_handler(handler, streams=[])[source]

Identify stream handlers writing to the given streams(s).

  • handler – The Handler class to check.
  • streams – A sequence of streams to match (defaults to matching stdout and stderr).

True if the handler is a StreamHandler logging to the given stream(s), False otherwise.

This function can be used as a callback for find_handler().


Walk through the propagation hierarchy of the given logger.

Parameters:logger – The logger whose hierarchy to walk (a Logger object).
Returns:A generator of Logger objects.


This uses the undocumented logging.Logger.parent attribute to find higher level loggers, however it won’t raise an exception if the attribute isn’t available.

class coloredlogs.ColoredFormatter(fmt=None, datefmt=None, level_styles=None, field_styles=None)[source]

Log Formatter that uses ANSI escape sequences to create colored logs.

__init__(fmt=None, datefmt=None, level_styles=None, field_styles=None)[source]

Initialize a ColoredFormatter object.


This initializer uses colorize_format() to inject ANSI escape sequences in the log format string before it is passed to the initializer of the base class.


Rewrite a logging format string to inject ANSI escape sequences.

Parameters:fmt – The log format string.
Returns:The logging format string with ANSI escape sequences.

This method takes a logging format string like the ones you give to logging.Formatter, splits it into whitespace separated tokens and then processes each token as follows:

It looks for %(...) field names in the token (from left to right). For each field name it checks if the field name has a style defined in the field_styles dictionary. The first field name that has a style defined determines the style for the complete token.

As an example consider the default log format (DEFAULT_LOG_FORMAT):

%(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s

The default field styles (DEFAULT_FIELD_STYLES) define a style for the name field but not for the process field, however because both fields are part of the same whitespace separated token they’ll be highlighted together in the style defined for the name field.


Apply level-specific styling to log records.

Parameters:record – A LogRecord object.
Returns:The result of logging.Formatter.format().

This method injects ANSI escape sequences that are specific to the level of each log record (because such logic cannot be expressed in the syntax of a log format string). It works by making a copy of the log record, changing the msg field inside the copy and passing the copy into the format() method of the base class.

class coloredlogs.HostNameFilter(use_chroot=True)[source]

Log filter to enable the %(hostname)s format.

Python’s logging module doesn’t expose the system’s host name while I consider this to be a valuable addition. Fortunately it’s very easy to expose additional fields in format strings: filter() simply sets the hostname attribute of each LogRecord object it receives and this is enough to enable the use of the %(hostname)s expression in format strings.

You can install this log filter as follows:

>>> import coloredlogs, logging
>>> handler = logging.StreamHandler()
>>> handler.addFilter(coloredlogs.HostNameFilter())
>>> handler.setFormatter(logging.Formatter('[%(hostname)s] %(message)s'))
>>> logger = logging.getLogger()
>>> logger.addHandler(handler)
>>> logger.setLevel(logging.INFO)
>>>"Does it work?")
[peter-macbook] Does it work?

Of course coloredlogs.install() does all of this for you :-).

classmethod install(handler, fmt=None, use_chroot=True)[source]

Install the HostNameFilter on a log handler (only if needed).

  • handler – The logging handler on which to install the filter.
  • fmt – The log format string to check for %(hostname).
  • use_chroot – Refer to find_hostname().

If fmt is given the filter will only be installed if fmt contains %(programname). If fmt is not given the filter is installed unconditionally.


Initialize a HostNameFilter object.

Parameters:use_chroot – Refer to find_hostname().

Set each LogRecord‘s hostname field.

class coloredlogs.ProgramNameFilter(programname=None)[source]

Log filter to enable the %(programname)s format.

Python’s logging module doesn’t expose the name of the currently running program while I consider this to be a useful addition. Fortunately it’s very easy to expose additional fields in format strings: filter() simply sets the programname attribute of each LogRecord object it receives and this is enough to enable the use of the %(programname)s expression in format strings.

Refer to HostNameFilter for an example of how to manually install these log filters.

classmethod install(handler, fmt, programname=None)[source]

Install the ProgramNameFilter (only if needed).

  • fmt – The log format string to check for %(programname).
  • handler – The logging handler on which to install the filter.
  • programname – Refer to __init__().

Initialize a ProgramNameFilter object.

Parameters:programname – The program name to use (defaults to the result of find_program_name()).

Set each LogRecord‘s programname field.

class coloredlogs.NameNormalizer[source]

Responsible for normalizing field and level names.


Initialize a NameNormalizer object.


Normalize a field or level name.

Parameters:name – The field or level name (a string).
Returns:The normalized name (a string).

Transforms all strings to lowercase and resolves level name aliases (refer to find_level_aliases()) to their canonical name:

>>> from coloredlogs import NameNormalizer
>>> from humanfriendly import format_table
>>> nn = NameNormalizer()
>>> sample_names = ['DEBUG', 'INFO', 'WARN', 'WARNING', 'ERROR', 'FATAL', 'CRITICAL']
>>> print(format_table([(n, nn.normalize_name(n)) for n in sample_names]))
| DEBUG    | debug    |
| INFO     | info     |
| WARN     | warning  |
| WARNING  | warning  |
| ERROR    | error    |
| FATAL    | critical |
| CRITICAL | critical |

Normalize the keys of a dictionary using normalize_name().

Parameters:value – The dictionary to normalize.
Returns:A dictionary with normalized keys.
get(normalized_dict, name)[source]

Get a value from a dictionary after normalizing the key.

  • normalized_dict – A dictionary produced by normalize_keys().
  • name – A key to normalize and get from the dictionary.

The value of the normalized key (if any).

The coloredlogs.converter module

Convert text with ANSI escape sequences to HTML.

coloredlogs.converter.capture(command, encoding='UTF-8')[source]

Capture the output of an external command as if it runs in an interactive terminal.

  • command – The command name and its arguments (a list of strings).
  • encoding – The encoding to use to decode the output (a string).

The output of the command.

This function runs an external command under script (emulating an interactive terminal) to capture the output of the command as if it was running in an interactive terminal (including ANSI escape sequences).


Convert text with ANSI escape sequences to HTML.

Parameters:text – The text with ANSI escape sequences (a string).
Returns:The text converted to HTML (a string).

Encode whitespace so that web browsers properly render it.

Parameters:text – The plain text (a string).
Returns:The text converted to HTML (a string).

The purpose of this function is to encode whitespace in such a way that web browsers render the same whitespace regardless of whether ‘preformatted’ styling is used (by wrapping the text in a <pre>...</pre> element).


Encode characters with a special meaning as HTML.

Parameters:text – The plain text (a string).
Returns:The text converted to HTML (a string).

The coloredlogs.syslog module

Easy to use UNIX system logging for Python’s logging module.

Admittedly system logging has little to do with colored terminal output, however:

  • The coloredlogs package is my attempt to do Python logging right and system logging is an important part of that equation.
  • I’ve seen a surprising number of quirks and mistakes in system logging done in Python, for example including %(asctime)s in a format string (the system logging daemon is responsible for adding timestamps and thus you end up with duplicate timestamps that make the logs awful to read :-).
  • The %(programname)s filter originated in my system logging code and I wanted it in coloredlogs so the step to include this module wasn’t that big.
  • As a bonus this Python module now has a test suite and proper documentation.

So there :-P. Go take a look at enable_system_logging().

coloredlogs.syslog.LOG_DEVICE_MACOSX = '/var/run/syslog'

The pathname of the log device on Mac OS X (a string).

coloredlogs.syslog.LOG_DEVICE_UNIX = '/dev/log'

The pathname of the log device on Linux and most other UNIX systems (a string).

coloredlogs.syslog.DEFAULT_LOG_FORMAT = '%(programname)s[%(process)d]: %(levelname)s %(message)s'

The default format for log messages sent to the system log (a string).

The %(programname)s format requires ProgramNameFilter but enable_system_logging() takes care of this for you.

The name[pid]: construct (specifically the colon) in the format allows rsyslogd to extract the $programname from each log message, which in turn allows configuration files in /etc/rsyslog.d/*.conf to filter these log messages to a separate log file (if the need arises).

class coloredlogs.syslog.SystemLogging(*args, **kw)[source]

Context manager to enable system logging.

__init__(*args, **kw)[source]

Initialize a SystemLogging object.


Enable system logging when entering the context.

__exit__(exc_type=None, exc_value=None, traceback=None)[source]

Disable system logging when leaving the context.


If an exception is being handled when we leave the context a warning message including traceback is logged before system logging is disabled.

coloredlogs.syslog.enable_system_logging(programname=None, fmt=None, logger=None, reconfigure=True, **kw)[source]

Redirect logging messages to the system log (e.g. /var/log/syslog).


A SysLogHandler object or None. If an existing handler is found and reconfigure is False the existing handler object is returned. If the connection to the system logging daemon fails None is returned.


When the logger’s effective level is too restrictive it is relaxed (refer to notes about log levels for details).

coloredlogs.syslog.connect_to_syslog(address=None, facility=None, level=None)[source]

Create a SysLogHandler.


A SysLogHandler object or None (if the system logging daemon is unavailable).

The process of connecting to the system logging daemon goes as follows:

  • If SysLogHandler supports the socktype option (it does since Python 2.7) the following two socket types are tried (in decreasing preference):

    1. SOCK_RAW avoids truncation of log messages but may not be supported.
    2. SOCK_STREAM (TCP) supports longer messages than the default (which is UDP).
  • If socket types are not supported Python’s (2.6) defaults are used to connect to the selected address.


Find the most suitable destination for system log messages.

Returns:The pathname of a log device (a string) or an address/port tuple as supported by SysLogHandler.

On Mac OS X this prefers LOG_DEVICE_MACOSX, after that LOG_DEVICE_UNIX is checked for existence. If both of these device files don’t exist the default used by SysLogHandler is returned.