登录csv文件的正确方法是啥?

Posted

技术标签:

【中文标题】登录csv文件的正确方法是啥?【英文标题】:what is the proper way to do logging in csv file?登录csv文件的正确方法是什么? 【发布时间】:2013-11-14 22:03:37 【问题描述】:

我想以格式化的形式记录发送到繁忙的 http 服务器的每个请求的一些信息,使用日志模块会创建一些我不想要的东西:

[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)

由于每次都会打开和关闭一个文件,我怕这样会拖慢整个服务器的性能,我该怎么办?

【问题讨论】:

您应该使用具有输出 csv 行格式的 logging.Formatter 实例。 【参考方案1】:

只需使用 python 的 logging 模块。

您可以根据需要调整输出;看看Changing the format of displayed messages:

要更改用于显示消息的格式,您需要指定要使用的格式:

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

和Formatters:

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

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


如果你想生成一个有效的 csv 文件,也可以使用 python 的csv module。

这是一个简单的例子:

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

输出:

"DEBUG","此消息应出现在控制台上" "INFO","""this"" 也应该这样,而且它正在使用引用..." "警告","还有这个"

【讨论】:

我花了 2 个小时才意识到 logging.basicConfig(level=logging.DEBUG) 是记录例如 INFO 级别日志所必需的...即使您将 propagate=False 设置为记录器。【参考方案2】:

正如树懒建议的那样,您可以轻松地将日志的分隔符编辑为逗号,从而生成 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.')

【讨论】:

有没有办法添加 CSV 标题行? (即 CSV 文本文件中包含列名的第一行?) 是的,这可能就是你要找的东西:***.com/questions/27840094/… 这个解决方案不够健壮——当asctime 中输出逗号时会发生什么?如果消息本身包含逗号或换行符怎么办?然后 CSV 文件被破坏。任何写入 CSV 数据的东西都应该通过 csv.writer 实例来完成,如其他一些答案所示。【参考方案3】:

我同意您应该使用日志记录模块,但是您不能像其他一些答案显示的那样仅使用格式字符串来正确执行此操作,因为它们没有解决您记录包含以下内容的消息的情况一个逗号。

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

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 - write data into csv format as string (not file)

【讨论】:

【参考方案4】:

我认为这不是最好的主意,但它是可行的,而且非常简单。 手动缓冲您的日志。将日志条目存储在某个地方,并不时将它们写入文件。 如果您知道您的服务器会一直很忙,请在缓冲区达到一定大小时刷新缓冲区。如果在使用上可能存在很大差距,我会说新线程(或更好的进程,检查自己为什么线程会吸收并减慢应用程序)具有无限的(当然理论上)睡眠/刷新循环会更好。 另外,请记住创建某种钩子,当服务器中断或失败时会刷新缓冲区(可能是信号?或者只是在 main 函数上尝试/除外 - 还有更多方法可以做到),这样你就不会丢失未刷新的缓冲区意外退出的数据。

我再说一遍 - 这不是最好的主意,这是我想到的第一件事。您可能想咨询 Flask 或其他一些 webapp 框架的日志实现(AFAIR Flask 也有 CSV 日志)。

【讨论】:

以上是关于登录csv文件的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将大型 CSV 文件加载到核心数据中的最快方法是啥

按列合并多个 csv 文件的最快方法是啥?

读取 .csv 文件时在 Python 中解析日期的最快方法是啥?

将CSV文件数据读取为命名元组行的pythonic方法是啥?

将不同长度的numpy数组保存到同一个csv文件的最佳方法是啥?

从 .CSV 文件比较/插入/更新 MySQL 数据库中的产品的最佳方法是啥