问题描述
我正在尝试使用 TimedRotatingFileHandler 将每日日志保存在单独的日志文件中.旋转可以按预期工作,但我不喜欢旋转的方式是文件的命名.
I am trying to use TimedRotatingFileHandler to keep daily logs in separate log files.The rotation works perfectly as intended, but what I don't like how it does is the naming of the files.
如果我将日志文件设置为my_log_file.log,则它将是今天的"日志文件,当它在午夜改变时,它将被重命名为my_log_file.log.2014-07-08
,结尾没有.log扩展名,并且将为新的一天创建新的my_log_file.log
.
If I set a log file as my_log_file.log, this will be the "today's" log file, and when it changes day at midnight it will be renamed to my_log_file.log.2014-07-08
with no .log extension at the end, and a new my_log_file.log
will be created for the new day.
我想得到的是将旧文件重命名为my_log_file.2014-07-08.log
甚至my_log_file-2014-07-08.log
,主要是将.log放在结尾,而不是放在中间.另外,我想已经用今天的日期来命名今天的"日志文件,就像旧的一样.
What I would like to get is the old file being renamed to my_log_file.2014-07-08.log
or even my_log_file-2014-07-08.log
, mainly with the .log at the end, not in the middle.Also, I would like to have the "today's" log file being already named with the today's date, just as the old ones.
有什么办法吗?
我发现我可以通过以下方式个性化后缀:
I found that I can personalize the suffix with:
但是我无法删除内部.log部分并强制当前日志文件添加后缀.
But I do not get the way to remove the inner .log part and to force the current log file to have the suffix added.
推荐答案
我创建了一个ParallelTimedRotatingFileHandler类,其主要目的是允许多个进程并行写入日志文件.此类解决的并行处理问题是:
I have created a class ParallelTimedRotatingFileHandler mainly aimed at allowing multiple processes writing in parallel to a log file.The problems with parallel processes solved by this class, are:
- 所有进程尝试同时复制或重命名同一文件时的过渡时刻会产生错误.
- 该问题的解决方案正是您建议的命名约定.因此,对于您在处理程序中提供的文件名
Service
,日志记录不会转到例如.Service.log
,但今天到Service.2014-08-18.log
,明天到Service.2014-08-19.log
. - 另一种解决方案是以
a
(附加)模式而不是w
模式打开文件以允许并行写入. - 删除备份文件也需要谨慎,因为多个并行进程正在同时删除同一文件.
- 此实现未考虑leap秒(对于Unix而言,这不是问题).在其他操作系统上,过渡时间仍然可能是30/6/2008 23:59:60,因此日期没有更改,因此,我们使用与昨天相同的文件名.
- 我知道标准的Python建议是不为并行进程预见
logging
模块,我应该使用SocketHandler,但至少在我的环境中,这是可行的.
- The rollover moment when all processes are trying to copy or rename the same file at the same time, gives errors.
- The solution for this problem was exactly the naming convention you suggest. So, for a file name
Service
that you supply in the handler, logging does not go to e.g.Service.log
but today toService.2014-08-18.log
and tomorrowService.2014-08-19.log
. - Another solution is to open the files in
a
(append) mode instead ofw
to allow parallel writes. - Deleting the backup files also needs to be done with caution as multiple parallel processes are deleting the same files at the same time.
- This implementation does not take into account leap seconds (which is not a problem for Unix). In other OS, it might still be 30/6/2008 23:59:60 at the rollover moment, so the date has not changed, so, we take the same file name as yesterday.
- I know that the standard Python recommendation is that the
logging
module is not foreseen for parallel processes, and I should use SocketHandler, but at least in my environment, this works.
该代码只是标准Python handlers.py模块中代码的微小变化.当然是版权所有者的版权.
The code is just a slight variation of the code in the standard Python handlers.py module. Of course copyright to the copyright holders.
这是代码:
import logging
import logging.handlers
import os
import time
import re
class ParallelTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, postfix = ".log"):
self.origFileName = filename
self.when = when.upper()
self.interval = interval
self.backupCount = backupCount
self.utc = utc
self.postfix = postfix
if self.when == 'S':
self.interval = 1 # one second
self.suffix = "%Y-%m-%d_%H-%M-%S"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
elif self.when == 'M':
self.interval = 60 # one minute
self.suffix = "%Y-%m-%d_%H-%M"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
elif self.when == 'H':
self.interval = 60 * 60 # one hour
self.suffix = "%Y-%m-%d_%H"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$"
elif self.when == 'D' or self.when == 'MIDNIGHT':
self.interval = 60 * 60 * 24 # one day
self.suffix = "%Y-%m-%d"
self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
elif self.when.startswith('W'):
self.interval = 60 * 60 * 24 * 7 # one week
if len(self.when) != 2:
raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
if self.when[1] < '0' or self.when[1] > '6':
raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
self.dayOfWeek = int(self.when[1])
self.suffix = "%Y-%m-%d"
self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
else:
raise ValueError("Invalid rollover interval specified: %s" % self.when)
currenttime = int(time.time())
logging.handlers.BaseRotatingHandler.__init__(self, self.calculateFileName(currenttime), 'a', encoding, delay)
self.extMatch = re.compile(self.extMatch)
self.interval = self.interval * interval # multiply by units requested
self.rolloverAt = self.computeRollover(currenttime)
def calculateFileName(self, currenttime):
if self.utc:
timeTuple = time.gmtime(currenttime)
else:
timeTuple = time.localtime(currenttime)
return self.origFileName + "." + time.strftime(self.suffix, timeTuple) + self.postfix
def getFilesToDelete(self, newFileName):
dirName, fName = os.path.split(self.origFileName)
dName, newFileName = os.path.split(newFileName)
fileNames = os.listdir(dirName)
result = []
prefix = fName + "."
postfix = self.postfix
prelen = len(prefix)
postlen = len(postfix)
for fileName in fileNames:
if fileName[:prelen] == prefix and fileName[-postlen:] == postfix and len(fileName)-postlen > prelen and fileName != newFileName:
suffix = fileName[prelen:len(fileName)-postlen]
if self.extMatch.match(suffix):
result.append(os.path.join(dirName, fileName))
result.sort()
if len(result) < self.backupCount:
result = []
else:
result = result[:len(result) - self.backupCount]
return result
def doRollover(self):
if self.stream:
self.stream.close()
self.stream = None
currentTime = self.rolloverAt
newFileName = self.calculateFileName(currentTime)
newBaseFileName = os.path.abspath(newFileName)
self.baseFilename = newBaseFileName
self.mode = 'a'
self.stream = self._open()
if self.backupCount > 0:
for s in self.getFilesToDelete(newFileName):
try:
os.remove(s)
except:
pass
newRolloverAt = self.computeRollover(currentTime)
while newRolloverAt <= currentTime:
newRolloverAt = newRolloverAt + self.interval
#If DST changes and midnight or weekly rollover, adjust for this.
if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
dstNow = time.localtime(currentTime)[-1]
dstAtRollover = time.localtime(newRolloverAt)[-1]
if dstNow != dstAtRollover:
if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
newRolloverAt = newRolloverAt - 3600
else: # DST bows out before next rollover, so we need to add an hour
newRolloverAt = newRolloverAt + 3600
self.rolloverAt = newRolloverAt
这篇关于如何使用python的TimedRotatingFileHandler强制旋转名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!