python 多进程日志打印方法
python 多进程日志轮转
具体代码
继承 logging.handlers.BaseRotatingHandler
轮转时,通过_open函数创建一个新的文件,文件名称总是真实写入的文件,没有文件的移动删除等操作
实际看到的基础文件,通过软链创建,用于日志收集、分析使用
轮转时间根据 suffix 决定,在日志整点时自动写入新的文件中
写入的mode必须是 append => 'a' , 保证每次写入到尾部是原子操作
class AlignedTimedRotatingFileHandler(logging.handlers.BaseRotatingHandler):
"""
自定义的定时轮转处理器,确保在每小时整点进行轮转
多进程安全
see: https://my.oschina.net/lionets/blog/796438
"""
def __init__(
self,
filename,
backupCount=0,
encoding=None,
delay=False,
):
# 设置文件名后缀
self.suffix = "%Y%m%d%H"
self.extMatch = re.compile(r"^\d{4}\d{2}\d{2}\d{2}$")
self.baseFilename = filename
self.currentFileName = self._compute_fn()
self.backupCount = backupCount
# 调用父类初始化方法 追加写入文件
super().__init__(filename, "a", encoding, delay)
def shouldRollover(self, record):
"""
根据文件名计算是否需要轮转
"""
if self.currentFileName != self._compute_fn():
return True
return False
def doRollover(self):
"""
执行轮转操作,仅将文件名修改,真正的轮转在 _open 函数中
"""
if self.stream:
self.stream.close()
self.stream = None
self.currentFileName = self._compute_fn()
# 如果需要删除文件,则删除
if self.backupCount > 0:
for s in self.getFilesToDelete():
try:
os.remove(s)
except Exception:
# 文件删除失败,忽略,可能是文件已经被删除或者没有权限
pass
def getFilesToDelete(self):
"""
获取需要删除的文件
"""
# 获取所有与当前文件名+后缀匹配的文件
dirName, baseName = os.path.split(self.baseFilename)
fileNames = os.listdir(dirName)
result = []
# 构建文件名前缀
prefix = baseName + "."
plen = len(prefix)
# 遍历目录下的所有文件
for fileName in fileNames:
if fileName[:plen] == prefix:
suffix = fileName[plen:]
if self.extMatch.match(suffix):
result.append(os.path.join(dirName, fileName))
# 如果文件数量小于等于backupCount,不需要删除任何文件
if len(result) <= self.backupCount:
return []
# 按文件名排序
result.sort()
# 返回需要删除的文件列表
return result[: len(result) - self.backupCount]
def _compute_fn(self):
"""
计算当前时间的文件名
"""
return self.baseFilename + "." + time.strftime(self.suffix, time.localtime())
def _open(self):
"""
重写 _open 方法,确保在轮转时多进程写入同一个文件
"""
if self.encoding is None:
stream = open(self.currentFileName, self.mode)
else:
stream = codecs.open(self.currentFileName, self.mode, self.encoding)
# 如果文件存在,则删除
if os.path.exists(self.baseFilename):
try:
os.remove(self.baseFilename)
except OSError:
pass
try:
os.symlink(self.currentFileName, self.baseFilename)
except OSError:
pass
return stream
参考
本文来自博客园,作者:吴丹阳-V,转载请注明原文链接:https://www.cnblogs.com/wudanyang/p/18824615

浙公网安备 33010602011771号