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

发布于 2024-10-11 02:22:07 字数 486 浏览 0 评论 0原文

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

我已经找到了这个:

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

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

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

I'm using the logging module in Python and I would like it to create a new logfile each time my application is started. The older logfiles shoud be rotated (eg: logfile.txt -> logfile1.txt, etc).

I already found this:

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

BaseRotatingHandler is the base class
for handlers that rotate log files at
a certain point. It is not meant to be
instantiated directly. Instead, use
RotatingFileHandler or
TimedRotatingFileHandler.

The RotatingFileHandler does a rollover at a predetermined size and the TimedRotatingFileHandler does a rollover based on the product of when and interval. Both are not what I want, I want the rotation to happen immediately when my application starts.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不寐倦长更 2024-10-18 02:22:07

我可能足以使用没有 maxBytesRotatingFileHandler ,然后在应用程序启动时调用 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)

I might be enough to use RotatingFileHandler without maxBytes, then call doRollover() on application start.

Yup, seems to work fine. The code below will create a new log file on each application run, with added timestamps for log start and close times. Running it will print the list of available log files. You can inspect them to check correct behavior. Adapted from the Python docs example:

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)
厌味 2024-10-18 02:22:07

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

例如

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 混合,以便为每个应用程序调用提供单独的日志,其中每个日志本身进一步分为多个固定大小的日志。

Simplest way is just to have a date tag in log file name, so when you start app each time you will get a new log file.

e.g.

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

so each time you will have log like myapp_2011-Jan-11_12-27-29.log

Another benefit is that you can mix this with RotatingFileHandler to have separate log for each app invocation, where each log itself is further divided into multiple fixed size logs.

本王不退位尔等都是臣 2024-10-18 02:22:07

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

Log Rotation and RoatatingFileHandler are usually designed and desirable when the application is running for a very long time (days) and you want the log to keep rotation. Under cases where I have to rotate the log upon restart of the application, I had to do that outside of the Logfile handler, which was easier. It was like, before the log writer call for the first time, I would see if the log file already existed, and if yes, rename it and create a new log file. The renaming should be differentiated from the handler's renaming mechanism.

‖放下 2024-10-18 02:22:07

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

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 > > ,这已被验证可以工作。 RotatingFileHandler 上的 0

  • 此方法尚未使用 TimedRotatingFileHandler 进行测试,但应该有效。

  • 此方法无需维护对要旋转的 RotatingFileHandler 的引用;因此,在使用 logging.config 配置日志记录时可以轻松使用它。

I had a similar requirement to be able to force a log rotation at startup based on a command-line option, but for the logfiles to otherwise rotate on their regular schedule. This was my solution:

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

Notes:

  • This has been validated to work if maxBytes > 0 on a RotatingFileHandler.

  • This method hasn't been tested with a TimedRotatingFileHandler, but should work.

  • This method eliminates the need to maintain a reference to the RotatingFileHandler to be rotated; as a result, it can easily be used when configuring logging using logging.config.

转瞬即逝 2024-10-18 02:22:07

正如 @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

日志旋转文件结构

As @Senthil Kumaran stated, this kind of logic is best put at the application level. Here's a full working example that sets up a logger that prints to stdout and writes to file. At application start up, if an existing log is found with the same name, it will be rotated out of the way in to a directory named 'old'. The RotatingFileHandler then handles rotating live logs when their size exceeds 512 bytes.

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")

The effect of running this on my system a few times gave the following directory and file structure:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文