Python日志记录在启动时移动文件

2024-04-19 10:43:48 发布

您现在位置:Python中文网/ 问答频道 /正文

在我的Flask应用程序中,我使用logging库实现了一个日志记录系统。它当前在以下函数中运行:


if __name__ == "__main__":
    """[Runs the webserver.
    Finally block is used for some logging management. It will first shut down
    logging, to ensure no files are open, then renames the file to 'log_'
    + the current date, and finally moves the file to the /logs archive
    directory]
    """
    try:
        session_management.clean_uploads_on_start(UPLOAD_FOLDER)
        app.run(debug=False)
    finally:
        try:
            logging.shutdown()
            new_log_file_name = log_management.rename_log(app.config['DEFAULT_LOG_NAME'])
            log_management.move_log(new_log_file_name)
        except FileNotFoundError:
            logging.warning("Current log file not found")
        except PermissionError:
            logging.warning("Permissions lacking to rename or move log.")

我发现,如果cmd提示符被强制关闭,或者服务器崩溃,文件不会被重命名和移动。我认为在服务器启动之前,最好将重命名和移动放在函数的初始“try”块中,但我遇到了问题,因为我有一个配置文件(在该脚本中导入),其中包含以下代码:

logging.basicConfig(filename='current_log.log', level=logging.INFO,
                    filemode='a',
                    format='%(asctime)s:%(levelname)s:%(message)s')

我尝试过做如下的事情,但仍然会遇到权限错误,但我认为仍然会遇到错误,因为日志管理脚本也会导入配置。此外,我找不到一个类似于logging.shutdown()的启动日志系统的函数,该函数在系统结束时使用,否则我会关闭它,移动文件(如果存在)并重新启动它


  try:
        session_management.clean_uploads_on_start(UPLOAD_FOLDER)
        log_management.check_log_on_startup(app.config['DEFAULT_LOG_NAME'])
        import config
        app.run(debug=False)
    finally:
        try:
            logging.shutdown()
            new_log_file_name = log_management.rename_log(app.config['DEFAULT_LOG_NAME'])
            log_management.move_log(new_log_file_name)
        except FileNotFoundError:
            logging.warning("Current log file not found")
        except PermissionError:
            logging.warning("Permissions lacking to rename or move log.")

# (in another script)
def check_log_on_startup(file_name):
    if os.path.exists(file_name):
        move_log(rename_log(file_name))

任何建议都很受欢迎,因为我感觉自己就像在一堵砖墙前


Tags: theto函数namelogconfigappnew
1条回答
网友
1楼 · 发布于 2024-04-19 10:43:48

正如您已经发现的,如果流程不干净地终止,那么在流程生命周期结束时尝试执行清理可能会失败

在开始时执行清理的问题是,在尝试移动旧日志文件之前,您显然从导入中调用了logging.basicConfig 这会导致在尝试重命名和移动现有日志时,隐式创建的FileHandler在现有日志上保存一个打开的文件对象。根据您使用的文件系统的不同,这可能不会令人高兴

如果您想将对可能的旧日志文件的处理完全移动到应用程序的开头,则必须在调用logging.basicConfig之前执行重命名和移动,因此您必须从导入中删除它并以某种方式将其添加到log_management

或者,您可以通过对标准FileHandler类进行子类化,将日志文件的整个处理过程移动到日志文件处理程序,例如:

import logging
import os
from datetime import datetime

class CustomFileHandler(logging.FileHandler):

    def __init__(self, filename, archive_path='archive', archive_name='log_%Y%m%d', **kwargs):
        self._archive = os.path.join(archive_path, archive_name)
        self._archive_log(filename)
        super().__init__(filename, **kwargs)

    def _archive_log(self, filepath):
        if os.path.exists(filepath):
            os.rename(filepath, datetime.now().strftime(self._archive))

    def close(self):
        super().close()
        self._archive_log(self.baseFilename)

使用此选项,您可以如下配置日志记录:

hdler = CustomFileHandler('current.log')
logging.basicConfig(level=logging.INFO, handlers=[hdler], 
                   format='%(asctime)s:%(levelname)s:%(message)s')

CustomFileHandler将在初始化期间检查并可能存档旧日志。这将处理不干净的流程终止后的遗留问题,其中无法进行关机清理。由于父类初始值设定项是在尝试日志存档后调用的,因此日志上还没有导致PermissionError的打开句柄

重写的close()方法将在干净的进程关闭时执行归档

这应该消除对专用log_management模块的需要,至少就代码中显示的函数而言是这样rename_logmove_logcheck_log_on_startup都封装在CustomFileHandler中。也不需要显式调用logging.shutdown()


一些注意事项:

找不到与logging.shutdown()等效的启动函数的原因是,在导入logging模块时,日志系统已启动/初始化。除其他外,它通过atexit实现instantiates the implicit root loggerregisters ^{} as exit handler
后者就是不需要用上述解决方案显式调用logging.shutdown()的原因。由于退出处理程序注册,Python解释器在准备解释器关闭时,将在完成过程中调用它logging.shutdown()然后遍历已注册处理程序的列表和calls their ^{} methods,这将在干净关闭期间执行日志存档

根据您选择的移动(和重命名)旧日志文件的方法,上述解决方案可能需要一些额外的异常保护措施^如果目标路径已经存在,{}将引发异常,即当您之前在同一天停止并启动了进程,而os.replace将以静默方式覆盖现有文件时。请参阅有关通过Pythonhere移动文件的更多详细信息

因此,我建议不仅按当前日期,而且按时间命名存档日志。
在上面,将当前日期添加到存档文件名是通过datetime^{}完成的,因此自定义文件处理程序的archive_name参数的默认值为“log{%Y%m%d”。前面带有%的字符是有效的format codes,由strftime()替换为调用它的datetime对象的相应部分。要将当前时间附加到存档日志文件名,只需将相应的格式代码附加到archive_name,例如:'log_%Y%m%d_strong>%H%m%S',这将产生一个日志名,如log_20200819_123721

相关问题 更多 >