如何在 Python 中临时更改记录消息的格式?

发布于 2024-11-26 09:50:45 字数 222 浏览 0 评论 0原文

在Python中(通过日志模块)临时更改日志消息格式的最简单方法是什么?

目标是拥有一些标准消息格式,同时能够临时添加有关正在读取的某些文件的信息(例如其名称);当不再读取文件时,消息格式应恢复为默认格式。生成消息的程序知道正在读取什么文件,因此如果它的消息自动包含相关文件名,那就太好了(错误消息将是:“ERROR while正在读取文件 ***:…”而不是“错误:…”)。

What is the simplest method for temporarily changing the logging message format, in Python (through the logging module)?

The goal is to have some standard message format, while being able to temporarily add information about some file being read (like its name); the message format should revert to its default when the file is not being read anymore. The program that produces the messages is not aware of what file is being read, so it would be nice if its message automatically included the relevant file name (the error message would be: "ERROR while reading file ***: …" instead of "ERROR: …").

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

樱花坊 2024-12-03 09:50:45

这是一个简单的解决方案,可以从 Vinay Sajip 自己的 HOWTO;它基本上使用 setFormatter() 更新日志格式化程序:

import logging

logger = logging.getLogger()  # Logger
logger_handler = logging.StreamHandler()  # Handler for the logger
logger.addHandler(logger_handler)

# First, generic formatter:
logger_handler.setFormatter(logging.Formatter('%(message)s'))
logger.error('error message')  # Test

# New formatter for the handler:
logger_handler.setFormatter(logging.Formatter('PROCESSING FILE xxx - %(message)s'))
logger.error('error message')  # Test

这会正确生成:(

error message
PROCESSING FILE xxx - error message

其中 xxx 可以按照问题中的要求动态设置为正在处理的文件)。

Here is a simple solution, that can be deduced from Vinay Sajip's own HOWTO; it basically updates the logging formatter with setFormatter():

import logging

logger = logging.getLogger()  # Logger
logger_handler = logging.StreamHandler()  # Handler for the logger
logger.addHandler(logger_handler)

# First, generic formatter:
logger_handler.setFormatter(logging.Formatter('%(message)s'))
logger.error('error message')  # Test

# New formatter for the handler:
logger_handler.setFormatter(logging.Formatter('PROCESSING FILE xxx - %(message)s'))
logger.error('error message')  # Test

This correctly produces:

error message
PROCESSING FILE xxx - error message

(where xxx can be set dynamically to the file being processed, as asked for in the question).

眉目亦如画i 2024-12-03 09:50:45

有几种方法。除了已经记录的< /a> (记录调用的extra 参数、LoggerAdapterFilter),另一种方法是指定自定义格式化类,您可以使用其实例可以随时了解正在处理的文件。例如:

class FileProcessingFormatter(logging.Formatter):
    def __init__(self, fmt, datefmt=None, current_file=None):
        super(FileProcessingFormatter, self).__init__(fmt, datefmt)
        self.orig_fmt = fmt
        self.current_file = current_file

    def format(self, record):
        if self.current_file is None:
            self._fmt = self.orig_fmt.replace('__FILE_PLACEHOLDER__', '')
        else:
            self._fmt = self.orig_fmt.replace('__FILE_PLACEHOLDER__',
                            ' while processing %r' % self.current_file)
        return super(FileProcessingFormatter, self).format(record)

实例化格式化程序...

f = FileProcessingFormatter('%(levelname)s__FILE_PLACEHOLDER__ %(message)s')
for h in relevant_handlers:
    h.setFormatter(f)

处理文件...

f.current_file = fn
process_file(fn)
f.current_file = None

这非常简单 - 例如,如果文件处理由不同线程同时完成,则不适用于线程环境。

更新:虽然根记录器的处理程序可以通过logging.getLogger().handlers访问,但这是一个可能会改变的实现细节。由于您的要求不是那么基本,您也许可以使用 dictConfig() 来配置日志记录(可通过 logutils 项目(适用于旧版本的 Python)。

There are several ways. Apart from the already documented ones (extra argument to logging calls, LoggerAdapter, Filter) , another way would be to specify a custom formatting class, whose instance you can keep informed about the file being processed. For example:

class FileProcessingFormatter(logging.Formatter):
    def __init__(self, fmt, datefmt=None, current_file=None):
        super(FileProcessingFormatter, self).__init__(fmt, datefmt)
        self.orig_fmt = fmt
        self.current_file = current_file

    def format(self, record):
        if self.current_file is None:
            self._fmt = self.orig_fmt.replace('__FILE_PLACEHOLDER__', '')
        else:
            self._fmt = self.orig_fmt.replace('__FILE_PLACEHOLDER__',
                            ' while processing %r' % self.current_file)
        return super(FileProcessingFormatter, self).format(record)

Instantiate the formatter ...

f = FileProcessingFormatter('%(levelname)s__FILE_PLACEHOLDER__ %(message)s')
for h in relevant_handlers:
    h.setFormatter(f)

Process files ...

f.current_file = fn
process_file(fn)
f.current_file = None

This is very simplistic - for example, not for use in threaded environments if file processing is done by different threads concurrently.

Update: Although the root logger's handlers are accessible via logging.getLogger().handlers, this is an implementation detail that could change. As your requirement is not that basic, you could perhaps use dictConfig() to configure your logging (available via the logutils project for older versions of Python).

归属感 2024-12-03 09:50:45

我不推荐这样做;但你可以说假设第一个根处理程序是搞砸的,

import logging
ROOT_LOGGER = logging.getLogger()
ROOT_LOGGER.handlers[0].setFormatter(logging.Formatter(
    '%(asctime)s:%(levelname)s:%(name)s:%(message)s\n'
))

如果你在任何具有托管日志记录的系统中,则可以直接修改它;这可能会让你的脚受伤;最好能够确定对要修改的处理程序的准确引用并对其进行修改;

但没人关心如果它工作正常的话它有多坏?/s

I don't recommend this; but you can say assume the first root handler is the one that's screwed up and modify it directly

import logging
ROOT_LOGGER = logging.getLogger()
ROOT_LOGGER.handlers[0].setFormatter(logging.Formatter(
    '%(asctime)s:%(levelname)s:%(name)s:%(message)s\n'
))

if you are in any system with managed logging; this is probably going to shoot your foot; it really would be best to be able to determine an exact reference to the handler you want to modify and modify that;

but nobody cares how broken it is if it works right?/s

凉墨 2024-12-03 09:50:45

据我所知,正如@Vinay Sajip 的更新中所提到的,dictConfig() 是最好的方法。

这是 Python 3.8 中的一个工作示例:

import logging
from logging.config import dictConfig    
FI_PATH_CONSUMERS_LOG = "/project/test_log.log"
LOG_FORMAT = "%(asctime)s %(levelname)-8s [%(name)s] %(message)s"
LOG_LEVEL = "INFO"
LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"

logging.basicConfig(
    filename=FI_PATH_CONSUMERS_LOG,
    format=LOG_FORMAT,
    level=getattr(logging, LOG_LEVEL),
    datefmt=LOG_DATEFMT,
)

def create_new_format(format_additions: str=""):
    """
    This adjusts both loggers, but you can
    also make changes specific to each.
    """
    alt_new_formats = {
        "event_format": {
            "format": LOG_FORMAT + format_additions,
            "datefmt": LOG_DATEFMT,
        },
    }

    changed_setting = {
        "version": 1,
        "formatters": alt_new_formats,
        "handlers": {
            "to_file": {
                "class": "logging.FileHandler",
                "filename": FI_PATH_CONSUMERS_LOG,
                "formatter": "event_format",
                "level": LOG_LEVEL,
            },
        },
        "loggers": {
            "": {
                "handlers": ["to_file"],
            },
        },
        "disable_existing_loggers": False,
    }
    return changed_setting

您可以像这样运行它:

logger = logging.getLogger()  # root logger
logger_2 = logging.getLogger("logger 2")  # child logger with name "logger 2"
msg_test = "Testing message."

logger.info(msg_test)
logger_2.info(msg_test)

dictConfig(create_new_format(" [Adjust formatting]"))
logger.info(msg_test)
logger_2.info(msg_test)

dictConfig(create_new_format())
logger.info(msg_test)
logger_2.info(msg_test)

并且它将给您以下输出:

2021-09-16 14:47:48 INFO     [root] Testing message.
2021-09-16 14:47:48 INFO     [logger 2] Testing message.
2021-09-16 14:47:48 INFO     [root] Testing message. [Adjust formatting]
2021-09-16 14:47:48 INFO     [logger 2] Testing message. [Adjust formatting]
2021-09-16 14:47:48 INFO     [root] Testing message.
2021-09-16 14:47:48 INFO     [logger 2] Testing message.

To my knowledge dictConfig(), as mentioned in the update by @Vinay Sajip, is the best way to go.

Here is a working example in Python 3.8:

import logging
from logging.config import dictConfig    
FI_PATH_CONSUMERS_LOG = "/project/test_log.log"
LOG_FORMAT = "%(asctime)s %(levelname)-8s [%(name)s] %(message)s"
LOG_LEVEL = "INFO"
LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"

logging.basicConfig(
    filename=FI_PATH_CONSUMERS_LOG,
    format=LOG_FORMAT,
    level=getattr(logging, LOG_LEVEL),
    datefmt=LOG_DATEFMT,
)

def create_new_format(format_additions: str=""):
    """
    This adjusts both loggers, but you can
    also make changes specific to each.
    """
    alt_new_formats = {
        "event_format": {
            "format": LOG_FORMAT + format_additions,
            "datefmt": LOG_DATEFMT,
        },
    }

    changed_setting = {
        "version": 1,
        "formatters": alt_new_formats,
        "handlers": {
            "to_file": {
                "class": "logging.FileHandler",
                "filename": FI_PATH_CONSUMERS_LOG,
                "formatter": "event_format",
                "level": LOG_LEVEL,
            },
        },
        "loggers": {
            "": {
                "handlers": ["to_file"],
            },
        },
        "disable_existing_loggers": False,
    }
    return changed_setting

Which you can run like so:

logger = logging.getLogger()  # root logger
logger_2 = logging.getLogger("logger 2")  # child logger with name "logger 2"
msg_test = "Testing message."

logger.info(msg_test)
logger_2.info(msg_test)

dictConfig(create_new_format(" [Adjust formatting]"))
logger.info(msg_test)
logger_2.info(msg_test)

dictConfig(create_new_format())
logger.info(msg_test)
logger_2.info(msg_test)

And which will give you the following output:

2021-09-16 14:47:48 INFO     [root] Testing message.
2021-09-16 14:47:48 INFO     [logger 2] Testing message.
2021-09-16 14:47:48 INFO     [root] Testing message. [Adjust formatting]
2021-09-16 14:47:48 INFO     [logger 2] Testing message. [Adjust formatting]
2021-09-16 14:47:48 INFO     [root] Testing message.
2021-09-16 14:47:48 INFO     [logger 2] Testing message.
童话 2024-12-03 09:50:45

如果你想动态改变日志的格式。
它可以做成这样。

logger = logging.getLogger()
# Change format of handler for the logger
logger.handlers[0].setFormatter(logging.Formatter('%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s'))
# Print log
logging.info(log)
# return other format
logger.handlers[0].setFormatter(logging.Formatter('%(message)s'))

if you want to dynamic change format of log.
it could be made like this.

logger = logging.getLogger()
# Change format of handler for the logger
logger.handlers[0].setFormatter(logging.Formatter('%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s'))
# Print log
logging.info(log)
# return other format
logger.handlers[0].setFormatter(logging.Formatter('%(message)s'))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文