简体   繁体   中英

How to set width of combined fields in Python logging

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.')

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM