如何使用python';什么是TimedRotatingFileHandler?

如何使用python';什么是TimedRotatingFileHandler?,python,logging,filehandler,Python,Logging,Filehandler,我正在尝试使用将每日日志保存在单独的日志文件中。 旋转工作完全符合预期,但我不喜欢它的工作方式是文件的命名 如果我将日志文件设置为my_log_file.log,这将是“今天”的日志文件,当它在午夜更改日期时,将重命名为my_log_file.log.2014-07-08,末尾没有.log扩展名,并且将为新的一天创建一个新的my_log_file.log 我想得到的是旧文件被重命名为 MyLogLogyField.2014-0708.日志或甚至 MyLogLogyFiel-2014-0708.

我正在尝试使用将每日日志保存在单独的日志文件中。 旋转工作完全符合预期,但我不喜欢它的工作方式是文件的命名

如果我将日志文件设置为my_log_file.log,这将是“今天”的日志文件,当它在午夜更改日期时,将重命名为
my_log_file.log.2014-07-08
,末尾没有.log扩展名,并且将为新的一天创建一个新的
my_log_file.log

我想得到的是旧文件被重命名为<代码> MyLogLogyField.2014-0708.日志或甚至<代码> MyLogLogyFiel-2014-0708. log ,主要是.log结尾,不在中间。 另外,我希望“今天”日志文件已经用今天的日期命名,就像旧的一样

有什么办法吗

我发现我可以通过以下方式对后缀进行个性化设置:

handler.suffix=“%Y-%m-%d”


但是我没有办法删除内部.log部分并强制当前日志文件添加后缀。

据我所知,没有办法直接实现这一点

您可以尝试的一种解决方案是覆盖默认行为

  • 创建自己的
    TimedRotatingFileHandler类
    并重写
    doRollover()函数。
  • 检查python安装中的源代码
    /Lib/logging/handlers.py
大概是这样的:

class MyTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, **kwargs):
        TimedRotatingFileHandler.__init__(self, **kwargs)

    def doRollover(self):
        # Do your stuff, rename the file as you want 

我创建了一个类ParallelTimedRotatingFileHandler,主要目的是允许多个进程并行写入日志文件。 此类解决的并行过程问题包括:

  • 当所有进程都试图同时复制或重命名同一文件时,会出现错误
  • 这个问题的解决方案正是您建议的命名约定。因此,对于您在处理程序中提供的文件名
    Service
    ,日志记录不会转到例如
    Service.log
    ,而是今天转到
    Service.2014-08-18.log
    ,明天转到
    Service.2014-08-19.log
  • 另一种解决方案是以
    a
    (append)模式而不是
    w
    打开文件,以允许并行写入
  • 删除备份文件时也需要小心,因为多个并行进程同时删除相同的文件
  • 此实现不考虑闰秒(这对于Unix来说不是问题)。在其他操作系统中,翻滚时刻可能仍然是30/6/2008 23:59:60,因此日期没有更改,因此,我们采用与昨天相同的文件名
  • 我知道Python的标准建议是,
    日志记录
    模块不适用于并行进程,我应该使用SocketHandler,但至少在我的环境中,这是可行的
该代码只是标准Python handlers.py模块中代码的一个微小变化。当然,版权归版权持有人所有

代码如下:

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
导入日志
导入日志记录处理程序
导入操作系统
导入时间
进口稀土
类ParallelTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def _uinit _;(self,filename,when='h',interval=1,backupCount=0,encoding=None,delay=False,utc=False,postfix=“.log”):
self.origFileName=文件名
self.when=when.upper()
self.interval=间隔
self.backupCount=备份计数
self.utc=utc
self.postfix=后缀
如果self.when==“S”:
self.interval=1#1秒
self.suffix=“%Y-%m-%d\u%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#一分钟
self.suffix=“%Y-%m-%d\u%H-%m”
self.extMatch=r“^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}$”
elif self.when==“H”:
self.interval=60*60#一小时
self.suffix=“%Y-%m-%d\uh”
self.extMatch=r“^\d{4}-\d{2}-\d{2}-\d{2}$”
elif self.when==“D”或self.when==“午夜”:
self.interval=60*60*24#一天
self.suffix=“%Y-%m-%d”
self.extMatch=r“^\d{4}-\d{2}-\d{2}$”
elif self.when.startswith('W'):
自我间隔=60*60*24*7#一周
如果len(self.when)!=2:
raise VALUERROR(“您必须指定从0到6的每周滚动日(0是星期一):%s”%self.when)
如果self.when[1]<“0”或self.when[1]>“6”:
raise VALUERROR(“为每周滚动指定的日期无效:%s”%self.when)
self.dayOfWeek=int(self.when[1])
self.suffix=“%Y-%m-%d”
self.extMatch=r“^\d{4}-\d{2}-\d{2}$”
其他:
raise VALUERROR(“指定的滚动间隔无效:%s”%self.when)
currenttime=int(time.time())
logging.handlers.BaseRotatingHandler.\uuuuu init\uuuuuuuu(self,self.calculateFileName(currenttime),'a',编码,延迟)
self.extMatch=re.compile(self.extMatch)
self.interval=self.interval*interval#乘以请求的单位
self.rolloverAt=self.computeRollover(当前时间)
def calculateFileName(自身,当前时间):
如果self.utc:
timeTuple=time.gmtime(currenttime)
其他:
timeTuple=time.localtime(currenttime)
返回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)
filename=os.listdir(dirName)
结果=[]
前缀=fName+”
后缀=self.postfix
prelen=len(前缀)
postlen=len(后缀)
对于文件名中的文件名:
如果文件名[:prelen]==前缀和文件名[-postlen:]==后缀
def __init__(self, filename, when='h', interval=1, backupCount=0,
             encoding=None, delay=False, utc=False, atTime=None, postfix = ".log"):
self.postfix = postfix
def namer(name):
    return name.replace(".log", "") + ".log"
handler.namer = namer
import os
import logging
from logging.handlers import TimedRotatingFileHandler
from config import constants

def namer(name):
    return name.replace(".log", "") + ".log"

def init(baseFilename):
    logPath = constants.LOGGING_DIR
    envSuffix = '-prod' if constants.ENV == 'prod' else '-dev'
    logFilename = os.path.join(logPath, baseFilename + envSuffix + '.log')
    print(f"Logging to {logFilename}")

    handler = TimedRotatingFileHandler(logFilename,
    when = "midnight", 
    backupCount = 30,
    encoding = 'utf8')
    handler.setLevel(logging.DEBUG)
    handler.suffix = "%Y%m%d"
    handler.namer = namer # <-- Here's where I assign the custom namer.

    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s [%(module)s:%(lineno)d]')
    handler.setFormatter(formatter)

    logging.basicConfig(
        handlers = [handler],
        format = '%(asctime)s %(levelname)s %(message)s [%(module)s:%(lineno)d]',
        level = logging.DEBUG,
        datefmt = '%Y-%m-%d %H:%M:%S')


if __name__ == '__main__':
    init('testing')
    logging.error("ohai")
    logging.debug("ohai debug")
    logging.getLogger().handlers[0].doRollover()
    logging.error("ohai next day")
    logging.debug("ohai debug next day")