简体   繁体   English

Python 中的多行日志记录

[英]Multi-line logging in Python

I'm using Python 3.3.5 and the logging module to log information to a local file (from different threads).我正在使用 Python 3.3.5 和日志记录模块将信息记录到本地文件(来自不同线程)。 There are cases where I'd like to output some additional information, without knowing exactly what that information will be (eg it might be one single line of text or a dict).在某些情况下,我想输出一些附加信息,但不知道该信息究竟是什么(例如,它可能是一行文本或一个字典)。

What I'd like to do is add this additional information to my log file, after the log record has been written.我想做的是在写入日志记录后将此附加信息添加到我的日志文件中。 Furthermore, the additional info is only necessary when the log level is error (or higher).此外,仅当日志级别为错误(或更高)时才需要附加信息。

Ideally, it would look something like:理想情况下,它看起来像:

2014-04-08 12:24:01 - INFO     - CPU load not exceeded
2014-04-08 12:24:26 - INFO     - Service is running
2014-04-08 12:24:34 - ERROR    - Could not find any active server processes
Additional information, might be several lines.
Dict structured information would be written as follows:
key1=value1
key2=value2
2014-04-08 12:25:16 - INFO     - Database is responding

Short of writing a custom log formatter, I couldn't find much which would fit my requirements.由于没有编写自定义日志格式化程序,我找不到太多符合我要求的内容。 I've read about filters and contexts, but again this doesn't seem like a good match.我已经阅读了有关过滤器和上下文的内容,但这似乎不是一个很好的匹配。

Alternatively, I could just write to a file using the standard I/O, but most of the functionality already exists in the Logging module, and moreover it's thread-safe.或者,我可以只使用标准 I/O 写入文件,但大多数功能已经存在于 Logging 模块中,而且它是线程安全的。

Any input would be greatly appreciated.任何投入将不胜感激。 If a custom log formatter is indeed necessary, any pointers on where to start would be fantastic.如果确实需要自定义日志格式化程序,那么任何关于从哪里开始的指针都会很棒。

Keeping in mind that many people consider a multi-line logging message a bad practice (understandably so, since if you have a log processor like DataDog or Splunk which are very prepared to handle single line logs, multi-line logs will be very hard to parse), you can play with the extra parameter and use a custom formatter to append stuff to the message that is going to be shown (take a look to the usage of 'extra' in the logging package documentation ).请记住,许多人认为多行日志消息是一种不好的做法(可以理解,因为如果您有像 DataDog 或 Splunk 这样非常准备处理单行日志的日志处理器,多行日志将很难parse),您可以使用extra参数并使用自定义格式化程序将内容附加到将要显示的消息中(查看日志包文档中 'extra' 的用法)。

import logging

class CustomFilter(logging.Filter):
    def filter(self, record):
        if hasattr(record, 'dct') and len(record.dct) > 0:
            for k, v in record.dct.iteritems():
                record.msg = record.msg + '\n\t' + k + ': ' + v
        return super(CustomFilter, self).filter(record)
        
if __name__ == "__main__":
    logging.getLogger().setLevel(logging.DEBUG)
    extra_logger = logging.getLogger('extra_logger')
    extra_logger.setLevel(logging.INFO)
    extra_logger.addFilter(CustomFilter())
    logging.debug("Nothing special here... Keep walking")
    extra_logger.info("This shows extra",
                      extra={'dct': {"foo": "bar", "baz": "loren"}})
    extra_logger.debug("You shouldn't be seeing this in the output")
    extra_logger.setLevel(logging.DEBUG)
    extra_logger.debug("Now you should be seeing it!")
    

That code outputs:该代码输出:

DEBUG:root:Nothing special here... Keep walking
INFO:extra_logger:This shows extra
        foo: bar
        baz: loren
DEBUG:extra_logger:Now you should be seeing it!

I still recommend calling the super 's filter function in your custom filter, mainly because that's the function that decides whether showing the message or not (for instance, if your logger's level is set to logging.INFO , and you log something using extra_logger.debug , that message shouldn't be seen, as shown in the example above)我仍然建议在您的自定义过滤器中调用superfilter函数,主要是因为这是决定是否显示消息的函数(例如,如果您的记录器级别设置为logging.INFO ,并且您使用extra_logger.debug记录某些extra_logger.debug ,不应看到该消息,如上例所示)

我只是在输出文本中添加了\\n符号。

It seems that I made a small typo when defining my LogFormatter string: by accidentally escaping the newline character, I wrongly assumed that writing multi-line output to a log file was not possible.似乎我在定义 LogFormatter 字符串时犯了一个小错误:由于不小心转义换行符,我错误地认为不可能将多行输出写入日志文件。

Cheers to @Barafu for pointing this out (which is why I assigned him the correct answer).为@Barafu 指出这一点干杯(这就是我为他分配正确答案的原因)。

Here's the sample code:这是示例代码:

import logging
lf = logging.Formatter('%(levelname)-8s - %(message)s\n%(detail)s')
lh = logging.FileHandler(filename=r'c:\temp\test.log')
lh.setFormatter(lf)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
log.addHandler(lh)
log.debug('test', extra={'detail': 'This is a multi-line\ncomment to test the formatter'})

The resulting output would look like this:结果输出如下所示:

DEBUG    - test
This is a multi-line
comment to test the formatter

Caveat:警告:

If there is no detail information to log, and you pass an empty string, the logger will still output a newline.如果没有要记录的详细信息,并且您传递了一个空字符串,则记录器仍将输出换行符。 Thus, the remaining question is: how can we make this conditional?因此,剩下的问题是:我们如何才能使这个有条件?

One approach would be to update the logging formatter before actually logging the information, as described here .一种方法是实际登录的信息之前更新的日志记录格式,如所描述这里

i'm using a simple line splitter in my smaller applications:我在较小的应用程序中使用了一个简单的分线器:

for line in logmessage.splitlines():
            writemessage = logtime + " - " + line + "\n"
            logging.info(str(writemessage))

Note that this is not thread-safe and should probably only be used in log-volume logging applications.请注意,这不是线程安全的,可能只应在日志卷日志记录应用程序中使用。

However you can output to log almost anything, as it will preserve your formatting.但是,您可以输出以记录几乎所有内容,因为它会保留您的格式。 I have used it for example to output JSON API responses formatted using: json.dumps(parsed, indent=4, sort_keys=True)例如,我使用它来输出使用以下格式的 JSON API 响应: json.dumps(parsed, indent=4, sort_keys=True)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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