如何正确地将日志记录到CSV文件?

26

我希望以格式化的形式记录每个发送到繁忙的HTTP服务器的请求的一些信息。使用 logging 模块会创建一些我不想要的内容:

[I 131104 15:31:29 Sys:34]

我考虑使用CSV格式,但是我不知道如何自定义它。Python有csv模块,但是我在手册中阅读到

import csv
with open('some.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(someiterable)

由于每次都要打开和关闭文件,我担心这种方法会降低整个服务器的性能。我该怎么办?


3
您应该使用一个 logging.Formatter 实例,并使用输出 csv 行的格式。 - Pedro Werneck
4个回答

28

只需使用Python的logging模块。

您可以按照自己的需要调整输出方式;请查看更改显示消息的格式

To change the format which is used to display messages, you need to specify the format you want to use:

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')

以及格式化程序:

格式化程序对象配置日志消息的最终顺序、结构和内容。

您可以在此处找到可用属性的列表:LogRecord属性


如果你想要生成一个有效的 csv 文件,也可以使用 Python 的 csv 模块。以下是一个简单的示例:
import logging
import csv
import io

class CsvFormatter(logging.Formatter):
    def __init__(self):
        super().__init__()
        self.output = io.StringIO()
        self.writer = csv.writer(self.output, quoting=csv.QUOTE_ALL)

    def format(self, record):
        self.writer.writerow([record.levelname, record.msg])
        data = self.output.getvalue()
        self.output.truncate(0)
        self.output.seek(0)
        return data.strip()

logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger(__name__)
logging.root.handlers[0].setFormatter(CsvFormatter())

logger.debug('This message should appear on the console')
logger.info('So should "this", and it\'s using quoting...')
logger.warning('And this, too')

输出:

"调试","这条消息应该出现在控制台上"
"信息","这也应该出现,它使用引号..."
"警告","这也是"


1
我花了2个小时才意识到,即使你将propagate=False设置为记录器,记录INFO级别日志时仍需要logging.basicConfig(level=logging.DEBUG) - Niloct
你如何将asctime添加到CsvFormatter中? - ramiwi

7

正如sloth所建议的那样,您可以轻松地将日志的分隔符编辑为逗号,从而生成CSV文件。

工作示例:

import logging

# create logger
lgr = logging.getLogger('logger name')
lgr.setLevel(logging.DEBUG) # log all escalated at and above DEBUG
# add a file handler
fh = logging.FileHandler('path_of_your_log.csv')
fh.setLevel(logging.DEBUG) # ensure all messages are logged to file

# create a formatter and set the formatter for the handler.
frmt = logging.Formatter('%(asctime)s,%(name)s,%(levelname)s,%(message)s')
fh.setFormatter(frmt)

# add the Handler to the logger
lgr.addHandler(fh)

# You can now start issuing logging statements in your code
lgr.debug('a debug message')
lgr.info('an info message')
lgr.warn('A Checkout this warning.')
lgr.error('An error writen here.')
lgr.critical('Something very critical happened.')

3
有没有一种方法可以添加CSV文件的首行呢?也就是在CSV文本文件中添加第一行,包含列名。 - ycomp
嗯,这可能是你正在寻找的内容:https://dev59.com/9Ibca4cB1Zd3GeqPZbdD - ecoe
4
这个解决方案不够健壮 - 如果 asctime 中输出了逗号会发生什么?如果消息本身包含逗号或换行符怎么办?CSV 文件将会损坏。任何写入 CSV 数据的东西都应该通过 csv.writer 实例来完成,就像其他答案中所示的那样。 - Alex Peters

6

我认为你应该使用日志模块,但是像其他回答中所示的仅使用格式化字符串的方式并不能完全解决记录包含逗号的消息的情况。

如果您需要一种能够正确转义消息或其他字段中的任何特殊字符的解决方案,您需要编写自定义格式化程序并设置它。

logger = logging.getLogger()

formatter = MyCsvFormatter()

handler = logging.FileHandler(filename, "w")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(level)

您显然需要实现MyCsvFormatter类,该类应继承自logging.Formatter并覆盖format()方法。

class MyCsvFormatter(logging.Formatter):
    def __init__(self):
        fmt = "%(levelname)s,%(message)s" # Set a format that uses commas, like the other answers
        super(MyCsvFormatter, self).__init__(fmt=fmt)

    def format(self, record):
        msg = record.getMessage()
        # convert msg to a csv compatible string using your method of choice
        record.msg = msg
        return super(MyCsvFormatter, self).format(self, record)

注意:我之前做过类似的事情,但没有测试过这个具体的代码示例。
在实际转义消息方面,以下是可能的一种方法: Python - 将数据写入 CSV 格式的字符串(而非文件)

0

我认为这不是最好的想法,但它是可行的,而且相当简单。 手动缓冲您的日志。将日志条目存储在某个地方,并定期将其写入文件。 如果您知道您的服务器将不断繁忙,请在达到某个大小时刷新缓冲区。如果可能存在使用间隙,我建议使用新线程(或更好的进程,请自行检查为什么线程会使应用程序变慢),其中包含无限(理论上当然)的睡眠/刷新循环。 此外,请记住创建某种钩子,在服务器中断或失败时刷新缓冲区(也许是信号?或者只是在主函数上尝试/除外 - 还有更多方法可以做到这一点),以便您不会失去未刷新的缓冲区数据在意外退出时。

我重申 - 这不是最好的想法,这是我想到的第一件事。您可能希望咨询Flask或其他Web应用程序框架的日志记录实现(AFAIR Flask也具有CSV日志记录)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接