6

In Python logging, is there a way to set the width of two combined fields?

I'm looking to combine filenames and line numbers to produce output similar to:

2011-10-14 13:47:51 main.py:12       DEBUG - Initializing cluster
2011-10-14 13:47:51 cluster.py:364   DEBUG - Starting cluster
2011-10-14 13:47:51 cluster.py:98    INFO  - Starting simulation
2011-10-14 13:47:51 simulation.py:79 DEBUG - Computing parameters

How would I modify the formatting string below, to achieve this?

logging.Formatter('%(asctime)s %(filename)s:%(lineno)s %(levelname)5s - %(msg)s 

UPDATE:

As @jonrsharpe points out, no out-of-the-box way to do this, so have customize the formatting.

Solution: @alecxe recommends a custom Filter, and here is a complete example:

import logging
import logging.config

class FilenameLinenoFilter(logging.Filter):
    def filter(self, record):
        record.filename_lineno = '{}:{}'.format(record.filename, record.lineno)
        return True

LOG_SETTINGS = {
    'version': 1,
    'filters': {
        'filename_lineno_filter': {
            '()': FilenameLinenoFilter,
        },
    },
    'formatters': {
        'standard': {
            'format': '%(asctime)s %(filename_lineno)-18s %(levelname)5s - %(msg)s',
        },
    },
    'handlers': {
        'default': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'filters': ['filename_lineno_filter'],
            'formatter': 'standard',
            },
    },
    'loggers': {
        '': { 
            'handlers': ['default'],
            'level': 'DEBUG',
        },
    }
}

logging.config.dictConfig(LOG_SETTINGS)
logger = logging.getLogger()
logger.debug('Debug output goes here.')
logger.info('Informational goes here.')
1
  • 1
    AFAIK there's no way to do that, unless you take over all of the formatting yourself. Commented Feb 8, 2016 at 20:26

2 Answers 2

6

A Filter is not the right tool for the job—filters are used to decide which log records get emitted and which are silenced, not to intercept log records just to "monkeypatch" them.

Rather, as of Python 3.2, you can use the logging.setRecordFactory function to register a custom LogRecord subclass or factory function to add a custom attribute to every LogRecord:

import logging

class CustomLogRecord(logging.LogRecord):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.origin = f"{self.filename}:{self.lineno}"

You can then set the width of this combined field in the format string for your logger:

logging.setLogRecordFactory(CustomLogRecord)
logging.basicConfig(
    style='{',
    format="{asctime} {origin:20} {levelname} - {message}",
    level=logging.INFO
)
logging.info("Test")
2020-06-26 16:10:03,193 scratch_2.py:16      INFO - Test
Sign up to request clarification or add additional context in comments.

1 Comment

The logging cookbook has a similar example that might be of interest docs.python.org/3/howto/…
5

You can make a combined field with the help of a custom filter:

import logging

class MyFilter(logging.Filter):
    def filter(self, record):
        record.filename_lineno = "%s:%d" % (record.filename, record.lineno)
        return True

Then, you can reference the %(filename_lineno)s placeholder in the formatting string.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.