indentedlogs

This library attempts to turn your logs into more readable form by addind indentation to the messages according to their location in the call stack. It also works well in conjunction with coloredlogs.

Let's see how these two libraries cooperate. As an example we will use demo.py script.

demo.py
def bump_version():
    pass


def configure():
    log.debug(f"Collecting parameters")
    log.debug(f"Ask for confirmation")
    log.info(f"Save configuration")


def select_target():
    log.warning(f"Default target selected")


def prepare():
    log.debug(f"Bump version")
    bump_version()
    log.debug(f"Generate configuration")
    configure()
    log.debug(f"Select target")
    select_target()


def call_compiler():
    log.debug(f"Preprocess sources")
    log.info(f"Compile sources")


def call_linker():
    log.info(f"Link objects")


def build_executable():
    # Note that callables that don't invoke logging
    # don't create additional indentation
    call_compiler()
    call_linker()


def build_package():
    log.warning(f"Symbols will be removed")
    log.debug(f"Strip binary")
    log.debug(f"Add meta data")
    log.debug(f"Create archive")


def upload_package():
    pass


def release_app():
    log.debug(f"Prepare")
    prepare()
    log.info(f"Build executable")
    build_executable()
    log.info(f"Build package")
    build_package()
    log.info(f"Upload package")
    upload_package()


release_app()

In order to make the script work, we need to obtain an instance of the logger and apply basic configuration. In the next step we will add indenting and coloring as demonstrated below.

import logging



fmt = '{asctime} {levelname:<8} {filename:>3}:{lineno:<3} {name:>10} {message}'
datefmt = '%Y-%m-%d %H:%M:%S'
logging.basicConfig(level='DEBUG', format=fmt, datefmt=datefmt, style='{')

log = logging.getLogger()
import logging
import indentedlogs


fmt = '{asctime} {levelname:<8} {filename:>3}:{lineno:<3} {name:>10} {message}'
datefmt = '%Y-%m-%d %H:%M:%S'
logging.basicConfig(level='DEBUG', format=fmt, datefmt=datefmt, style='{')
indentedlogs.install()
log = logging.getLogger()
import logging
import indentedlogs
import coloredlogs

fmt = '{asctime} {levelname:<8} {filename:>3}:{lineno:<3} {name:>10} {message}'

coloredlogs.install(level='DEBUG', fmt=fmt, style='{')
indentedlogs.install()  # must be called after coloredlogs
log = logging.getLogger()

Finally we can run three versions of the script and compare the results. Note that the actual color palette may differ from the one below.

2020-04-07 19:33:28 DEBUG    demo.py:59        root Prepare
2020-04-07 19:33:28 DEBUG    demo.py:23        root Bump version
2020-04-07 19:33:28 DEBUG    demo.py:25        root Generate configuration
2020-04-07 19:33:28 DEBUG    demo.py:13        root Collecting parameters
2020-04-07 19:33:28 DEBUG    demo.py:14        root Ask for confirmation
2020-04-07 19:33:28 INFO     demo.py:15        root Save configuration
2020-04-07 19:33:28 DEBUG    demo.py:27        root Select target
2020-04-07 19:33:28 WARNING  demo.py:19        root Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 DEBUG    demo.py:32        root Preprocess sources
2020-04-07 19:33:28 INFO     demo.py:33        root Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root Symbols will be removed
2020-04-07 19:33:28 DEBUG    demo.py:49        root Strip binary
2020-04-07 19:33:28 DEBUG    demo.py:50        root Add meta data
2020-04-07 19:33:28 DEBUG    demo.py:51        root Create archive
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package
2020-04-07 19:33:28 DEBUG    demo.py:59        root Prepare
2020-04-07 19:33:28 DEBUG    demo.py:23        root   Bump version
2020-04-07 19:33:28 DEBUG    demo.py:25        root   Generate configuration
2020-04-07 19:33:28 DEBUG    demo.py:13        root     Collecting parameters
2020-04-07 19:33:28 DEBUG    demo.py:14        root     Ask for confirmation
2020-04-07 19:33:28 INFO     demo.py:15        root     Save configuration
2020-04-07 19:33:28 DEBUG    demo.py:27        root   Select target
2020-04-07 19:33:28 WARNING  demo.py:19        root     Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 DEBUG    demo.py:32        root   Preprocess sources
2020-04-07 19:33:28 INFO     demo.py:33        root   Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root   Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root   Symbols will be removed
2020-04-07 19:33:28 DEBUG    demo.py:49        root   Strip binary
2020-04-07 19:33:28 DEBUG    demo.py:50        root   Add meta data
2020-04-07 19:33:28 DEBUG    demo.py:51        root   Create archive
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package
2020-04-07 19:33:28 DEBUG    demo.py:59        root Prepare
2020-04-07 19:33:28 DEBUG    demo.py:23        root   Bump version
2020-04-07 19:33:28 DEBUG    demo.py:25        root   Generate configuration
2020-04-07 19:33:28 DEBUG    demo.py:13        root     Collecting parameters
2020-04-07 19:33:28 DEBUG    demo.py:14        root     Ask for confirmation
2020-04-07 19:33:28 INFO     demo.py:15        root     Save configuration
2020-04-07 19:33:28 DEBUG    demo.py:27        root   Select target
2020-04-07 19:33:28 WARNING  demo.py:19        root     Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 DEBUG    demo.py:32        root   Preprocess sources
2020-04-07 19:33:28 INFO     demo.py:33        root   Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root   Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root   Symbols will be removed
2020-04-07 19:33:28 DEBUG    demo.py:49        root   Strip binary
2020-04-07 19:33:28 DEBUG    demo.py:50        root   Add meta data
2020-04-07 19:33:28 DEBUG    demo.py:51        root   Create archive
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package

indentedlogs determines indentation using indentation level, which is a measure of displacement. Followin rules apply while makeing indentations:

  • Indentation level of the first message is always 0.
  • Indentation level is calculated incrementaly from the relative position in the call stack.
  • Methods/functions used for logging (by default all the methods of logging.Logger class) are discarded in the call stack.
  • Indentation level can increase at most by 1 and decrease by any value between subsequent messages.
  • Indentation level is rounded up to the nearest integer.
  • Indentation level is coerced to 0..max_level range. Coercing from above the rane is indicated by special characters (by default ..).

In order to see how the algorithm works, let's change logging level from DEBUG to INFO.

2020-04-07 19:33:28 INFO     demo.py:15        root Save configuration
2020-04-07 19:33:28 WARNING  demo.py:19        root Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 INFO     demo.py:33        root Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root Symbols will be removed
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package
2020-04-07 19:33:28 INFO     demo.py:15        root Save configuration
2020-04-07 19:33:28 WARNING  demo.py:19        root Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 INFO     demo.py:33        root   Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root   Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root   Symbols will be removed
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package
2020-04-07 19:33:28 INFO     demo.py:15        root Save configuration
2020-04-07 19:33:28 WARNING  demo.py:19        root Default target selected
2020-04-07 19:33:28 INFO     demo.py:61        root Build executable
2020-04-07 19:33:28 INFO     demo.py:33        root   Compile sources
2020-04-07 19:33:28 INFO     demo.py:37        root   Link objects
2020-04-07 19:33:28 INFO     demo.py:63        root Build package
2020-04-07 19:33:28 WARNING  demo.py:48        root   Symbols will be removed
2020-04-07 19:33:28 INFO     demo.py:65        root Upload package

Please check API documentation for the details on how to fine-tune the algorithm.