需要使用Python实现每日日志轮换(0时区)。

6
我是一名Python新手。我写了一个小的日志记录器,可以从串口获取数据并将其写入日志文件中。我有一个小程序,它打开文件进行追加写入,然后关闭文件。我怀疑这可能不是最好的方法,但这是我目前想出来的方法。
我希望能够在UTC 00时自动执行日志轮换,但到目前为止,使用RotatingFileHandler尝试实现这一点失败了。
以下是代码示例:
import time, serial, logging, logging.handlers,os,sys
from datetime import *

CT12 = serial.Serial()
CT12.port = "/dev/ct12k"
CT12.baudrate = 2400
CT12.parity = 'E'
CT12.bytesize = 7
CT12.stopbits = 1
CT12.timeout = 3

logStart = datetime.now()
dtg = datetime.strftime(logStart, '%Y-%m-%d %H:%M:%S ')
ctlA = unichr(1)
bom = unichr(2)
eom = unichr(3)
bel = unichr(7)
CT12Name = [ctlA, 'CT12-NWC-test']
CT12Header = ['-Ceilometer Logfile \r\n', '-File created: ', dtg, '\r\n']

def write_ceilo ( text ) :
    f = open ('/data/CT12.log', 'a')
    f.write (text)
    f.close ()

write_ceilo(''.join(CT12Header))

CT12.open()

discard = CT12.readlines()
#print (discard)

while CT12.isOpen():
    response = CT12.readline()
    if len(response) >= 3:
        if response[0] == '\x02' :
            now=datetime.now()
            dtg=datetime.strftime(now, '-%Y-%m-%d %H:%M:%S\r\n')
            write_ceilo(dtg)
            write_ceilo(''.join(CT12Name))
            write_ceilo(response)

我该怎么做才能让它自动旋转,并附上旋转日期或序列号以供识别。我不想将它们中的任何一个旋转出去,只是保留每天的数据日志文件。(或者可能是每小时的文件?)


1
我建议你去了解一下logrotate。http://linuxcommand.org/man_pages/logrotate8.html - Lennart Regebro
当然有这个可能。我本来希望学习如何在Python中使用工具,但我可能会选择更容易(至少是我更理解的)的方式。 - GerryC
3个回答

21

对于通过Google到达的任何人,请不要在记录器正在使用时调用系统复制或移动命令等将日志文件移出记录器。

您需要的是定时轮换文件处理程序(TimedRotatingFileHandler)

import time

import logging
from logging.handlers import TimedRotatingFileHandler

# format the log entries
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s')

handler = TimedRotatingFileHandler('/path/to/logfile.log', 
                                   when='midnight',
                                   backupCount=10)
handler.setFormatter(formatter)
logger = logging.getLogger(__name__)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

# generate example messages
for i in range(10000):
    time.sleep(1)
    logger.debug('debug message')
    logger.info('informational message')
    logger.warn('warning')
    logger.error('error message')
    logger.critical('critical failure')


2
如果记录日志的 Python 代码或应用程序不持续运行,这段代码就无法运行。你知道解决方法吗? - Adarsh Trivedi
@AdarshTrivedi 当进程未运行时,日志不会被创建,但在下次启动进程时将适当地捕获(附加到当前日期的日志或根据需要创建新日期)。如果您需要始终运行的单独记录进程,可以将其与队列记录器组合使用,或使用操作系统的记录设施,但这两个选项应该是新问题。希望能有所帮助! - ZachP

0

对于那些不喜欢轮换文件的想法,而只想使用一个文件处理程序,立即将数据写入以特定日期命名的文件中:编写自己的处理程序并不难。以下是一个示例:

class FileHandlerWithOneFilePerPeriod(FileHandler):
"""A handler which writes formatted logging records to files, one file per period."""

def __init__(self, filename_pattern, mode='a', encoding=None, delay=False):
    """
    Constructs the file handler.

    :param filename_pattern: the filename. Use strftime() directives to specify the format of the period.
    For example, %Y%m%d can be used to generate one log file per day.
    :param mode: the mode to open the file before writing. Common values are 'w' for writing (truncating the file
    if it already exists), 'x' for creating and writing to a new file, and 'a' for appending (which on some Unix
    systems, means that all writes append to the end of the file regardless of the current seek position).
    :param encoding: encoding is the name of the encoding used to decode or encode the file. This should only be
    used in text mode.
    :param delay: True if the file is opened when the first log message is emitted; False if the file is opened now
    by the constructor.
    """
    self.filename_pattern = filename_pattern
    filename = datetime.now().strftime(self.filename_pattern)
    super().__init__(filename, mode, encoding, delay)

def emit(self, record: LogRecord):
    new_filename = datetime.fromtimestamp(record.created).strftime(self.filename_pattern)
    if self.stream is None:
        self.set_new_filename(new_filename)
    elif self.differs_from_current_filename(new_filename):
        self.close()
        self.set_new_filename(new_filename)

    super().emit(record)

def set_new_filename(self, new_filename):
    self.baseFilename = new_filename

def differs_from_current_filename(self, filename: str) -> bool:
    return filename != self.baseFilename

要使用此处理程序,请在字典中使用以下值进行配置(使用logging.config.dictConfig()):

version: 1
formatters:
  simple:
    format: '%(asctime)s %(name)s %(levelname)s %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
  file:
    class: my_package.my_module.FileHandlerWithOneFilePerPeriod
    level: DEBUG
    formatter: simple
    filename_pattern: my_logging-%Y%m%d.log
root:
  level: DEBUG
  handlers: [console, file]

这将记录到控制台和文件中。每天使用一个文件。更改my_packagemy_module以匹配您放置处理程序的模块。将my_logging更改为更合适的名称。

通过更改filename_pattern中的日期格式,您实际上控制了何时创建新文件。每次应用于日期时间的模式与之前应用的模式不同时,将创建一个新文件。


0
你可以简单地这样做:
import os
import time
date1 = time.strftime('%Y%m%d%H%M%S')
cmd1= "cp logfile logfile{0}".format(date1)
cmd2= "cat /dev/null > logfile"
os.system(cmd1)
os.system(cmd2)

'

'logfile'是文件名。我已将旧日志复制到一个以时间和日期命名的新日志文件中,然后清空了原始文件。如果您想每隔一小时循环一次,请将此脚本放入cron中。

'

谢谢,这个方法非常有效。由于我写入日志文件的方式,如果我只是将cmd1更改为'mv'而不是'cp',它会创建一个新的日志文件,我相信。虽然我希望能够学习一些Python中的日志工具知识,但有了这个方法,我可以花些时间进行实验。感谢您的提示! - GerryC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接