每次启动应用程序时轮换日志文件 (Python)

问题描述 投票:0回答:6

我正在Python中使用日志记录模块,我希望它在每次启动我的应用程序时创建一个新的日志文件。较旧的日志文件应轮换(例如:logfile.txt -> logfile1.txt 等)。

我已经找到了这个:

http://docs.python.org/library/logging.html

BaseRotatingHandler 是基类 对于轮换日志文件的处理程序 某一点。这并不意味着 直接实例化。相反,使用 旋转文件处理程序或 定时旋转文件处理程序。

RotatingFileHandler 以预定大小进行翻转,而 TimedRotatingFileHandler 根据时间和间隔的乘积进行翻转。两者都不是我想要的,我希望在我的应用程序启动时立即进行轮换。

python logging rotation
6个回答
44
投票

我可能足以使用

RotatingFileHandler
而不使用
maxBytes
,然后在应用程序启动时调用
doRollover()

是的,看起来效果很好。下面的代码将在每次应用程序运行时创建一个新的日志文件,并添加日志开始和关闭时间的时间戳。运行它将打印可用日志文件的列表。您可以检查它们以检查正确的行为。改编自 Python 文档示例:

import os
import glob
import logging
import logging.handlers
import time

LOG_FILENAME = 'logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Check if log exists and should therefore be rolled
needRoll = os.path.isfile(LOG_FILENAME)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, backupCount=50)

my_logger.addHandler(handler)

# This is a stale log, so roll it
if needRoll:    
    # Add timestamp
    my_logger.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())

    # Roll over on application start
    my_logger.handlers[0].doRollover()

# Add timestamp
my_logger.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())

# Log some messages
for i in xrange(20):
    my_logger.debug('i = %d' % i)

# See what files are created
logfiles = glob.glob('%s*' % LOG_FILENAME)

print '\n'.join(logfiles)

7
投票

最简单的方法就是在日志文件名中添加日期标签,这样当您每次启动应用程序时,您都会得到一个新的日志文件。

例如

dateTag = datetime.datetime.now().strftime("%Y-%b-%d_%H-%M-%S")
logging.basicConfig(filename="myapp_%s.log" % dateTag, level=logging.DEBUG)

所以每次你都会有像

myapp_2011-Jan-11_12-27-29.log

这样的日志

另一个好处是,您可以将其与 RotatingFileHandler 混合使用,为每个应用程序调用提供单独的日志,其中每个日志本身进一步分为多个固定大小的日志。


2
投票

Log Rotation 和 RoatatingFileHandler 通常是在应用程序运行很长时间(数天)并且您希望日志保持轮换时设计和需要的。在我必须在应用程序重新启动时轮换日志的情况下,我必须在日志文件处理程序之外执行此操作,这更容易。就像在日志编写器第一次调用之前,我会查看日志文件是否已经存在,如果是,则重命名它并创建一个新的日志文件。重命名应与处理程序的重命名机制区分开来。


0
投票

我有类似的要求,能够在启动时基于命令行选项强制进行日志轮换,但日志文件要按其常规计划进行轮换。这是我的解决方案:

import logging

from logging.handlers import BaseRotatingHandler
from typing import Union

def rotate_logs(loggers: Union[str,list]=None, delimiter: str=','):
    """Rotate logs.

    Args:
        loggers: List of logger names as list object or as string,
            separated by `delimiter`.

        delimiter: Separator for logger names, if `loggers` is :obj:`str`.
            Defaults to ``,`` (comma).

    """

    # Convert loggers to list.
    if isinstance(loggers, str):
        loggers = [t.strip() for t in loggers.split(delimiter)]

    handlers = []
    root = logging.getLogger()

    # Include root logger in dict.    
    ld = {'': root, **root.manager.loggerDict}

    for k, v in ld.items():
        if loggers is not None and k not in loggers:
            continue

        try:
            for h in v.handlers:
                if (isinstance(h, BaseRotatingHandler) and
                    h not in handlers):

                    handlers.append(h)

        except AttributeError:
            pass

    for h in handlers:
        h.doRollover()



if __name__ == '__main__':
    pass

备注:

  • 已验证如果

    maxBytes > 0
    RotatingFileHandler
    上可以工作。

  • 此方法尚未使用

    TimedRotatingFileHandler
    进行过测试,但应该有效。

  • 此方法无需维护对要旋转的

    RotatingFileHandler
    的引用;因此,在使用
    logging.config
    配置日志记录时可以轻松使用它。


0
投票

正如 @Senthil Kumaran 所说,这种逻辑最好放在应用程序级别。这是一个完整的工作示例,它设置了一个打印到标准输出并写入文件的记录器。在应用程序启动时,如果发现具有相同名称的现有日志,它将被轮换到名为“old”的目录中。然后,当日志大小超过 512 字节时,RotatingFileHandler 会处理旋转的实时日志。

import logging
import logging.handlers as handlers
from pathlib import Path
from typing import List


def configure_logger():
    file_handler = _get_file_handler()
    log_handlers: List[logging.Handler] = [logging.StreamHandler(), file_handler]
    logging.basicConfig(
        level="INFO",
        format="[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s",
        handlers=log_handlers,
    )


def _get_file_handler() -> handlers.RotatingFileHandler:
    log_path_str = "logs/my_app.log"
    log_path = Path(log_path_str)
    _rotate_existing_log(log_path)
    log_path.parent.mkdir(parents=True, exist_ok=True)
    return handlers.RotatingFileHandler(
        log_path,
        maxBytes=512,
        backupCount=5,
        # don't append to existing file, instead create a new
        mode="w",
    )


def _rotate_existing_log(log_path: Path):
    """
    If log file already exists, rotate it out of the way into an 'old' directory
    :param log_path:
    :return:
    """
    if log_path.exists():
        old_log_paths = log_path.parent / "old"
        old_log_paths.mkdir(parents=True, exist_ok=True)
        i = 1
        old_log_name = old_log_paths / (log_path.name + f".{i}")
        while old_log_name.exists():
            i += 1
            old_log_name = old_log_paths / (log_path.name + f".{i}")
        log_path.rename(old_log_name)


if __name__ == "__main__":
    configure_logger()
    logger = logging.getLogger(__name__)
    logger.info("hello world")

在我的系统上运行几次的效果给出了以下目录和文件结构:

C:\USERS\PYCHARMPROJECTS\SCRATCH\LOGS
|   my_app.log
|
\---old
        my_app.log.1
        my_app.log.2
        my_app.log.3
        my_app.log.4

log rotate file structure


0
投票

另一个解决方案是创建您自己的 Handler 类(特别是如果您使用 conf 文件)。

例如,如果您想在运行时使用当前日期时间创建一个新文件:

import logging
from datetime import datetime 

class ExecutionFileHandler(logging.FileHandler):
    def __init__(self, *args):
        current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        super().__init__(filename=f"{current_time}.log", *args)

因此你可以在conf文件中提及这个类

[handler_file]
class=execution_file_handler.ExecutionFileHandler

请注意,自定义类不能与加载配置的文件位于同一文件中(参见我无法获取logging.config来加载我的自定义类

© www.soinside.com 2019 - 2024. All rights reserved.