python多进程打日志的问题
出现的问题:
- 明明日志定义的是5个G,备份1个,结果却是文件和备份文件都只有10多M。
- 经常性的磁盘空间不足,实际占用并没有占那么多,而是程序占用文件的句柄,需要重启释放句柄才可以。
猜测问题和解决办法:
我们之前打日志用的RotatingFileHandler,这个是线程安全的,但是并不是进程安全的,我们使用gunicorn多进程的方式启动服务,所以猜测是单个文件被好几个进程写入,所以它到底该听谁的?是吧。
解决方案:
使用ConcurrentRotatingFileHandler
该ConcurrentRotatingFileHandler
类是一个下拉更换为Python的标准日志处理RotatingFileHandler
。此模块使用文件锁定,以便多个进程可以同时记录到单个文件,而不会丢弃或破坏日志事件。此模块提供与RotatingFileHanler
类似的文件循环方案。特别注意确保在旋转过程开始之前可以安全地旋转日志。(此模块解决Windows 上的RotatingFileHandler
文件重命名问题,其中旋转失败意味着删除所有后续日志事件)。
此模块尝试不惜一切代价保留日志记录。这意味着日志文件将大于指定的最大(旋转)大小。因此,如果磁盘空间紧张,您可能希望坚持使用RotatingFileHandler
,它将严格遵守最大文件大小。
如果您有多个脚本(或多个脚本)的实例同时运行并写入同一个日志文件,那么所有脚本都应该使用ConcurrentRotatingFileHandler
。您不应尝试混合和匹配RotatingFileHandler
和ConcurrentRotatingFileHandler
。
这个包捆绑了portalocker来处理文件锁定。请注意,portalocker目前仅支持Unix(posix)和NT平台,因此该软件包也仅支持这些平台。
使用以下命令安装此程序包:
pip install ConcurrentLogHandler
一个简单的事例:
from logging import getLogger, INFO
from cloghandler import ConcurrentRotatingFileHandler
import os
log = getLogger()
# Use an absolute path to prevent file rotation trouble.
logfile = os.path.abspath("mylogfile.log")
# Rotate log after reaching 512K, keep 5 old copies.
rotateHandler = ConcurrentRotatingFileHandler(logfile, "a", 512*1024, 5)
log.addHandler(rotateHandler)
log.setLevel(INFO)
log.info("Here is a very exciting log message, just for you")
如果您要分发代码并且不确定ConcurrentLogHandler
包是否已安装在代码运行的任何地方,那么Python可以轻松地优雅地回退到内置的RotatingFileHandler
,这是一个示例:
try:
from cloghandler import ConcurrentRotatingFileHandler as RFHandler
except ImportError:
# Next 2 lines are optional: issue a warning to the user
from warnings import warn
warn("ConcurrentLogHandler package not installed. Using builtin log handler")
from logging.handlers import RotatingFileHandler as RFHandler
log = getLogger()
rotateHandler = RFHandler("/path/to/mylogfile.log", "a", 1048576, 15)
log.addHandler(rotateHandler)
配置文件示例
此示例显示如何将此日志处理程序与日志记录配置文件解析器一起使用。这允许您将日志记录配置代码与应用程序代码分开。
配置文件示例: logging.ini
:
[loggers]
keys=root
[handlers]
keys=hand01
[formatters]
keys=form01
[logger_root]
level=NOTSET
handlers=hand01
[handler_hand01]
class=handlers.ConcurrentRotatingFileHandler
level=NOTSET
formatter=form01
args=("rotating.log", "a", 512*1024, 5)
[formatter_form01]
format=%(asctime)s %(levelname)s %(message)s
示例Python代码:app.py
:
import logging, logging.config
import cloghandler
logging.config.fileConfig("logging.ini")
log = logging.getLogger()
log.info("Here is a very exciting log message, just for you")