1 回答
TA贡献2012条经验 获得超12个赞
正如您已经发现的那样,如果进程不干净地终止,尝试在进程生命周期结束时执行清理可能会失败。
在开始时执行清理的问题是,您显然在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_log
,move_log
并且check_log_on_startup
都封装在CustomFileHandler
. 也无需显式调用logging.shutdown()
.
一些注意事项:
找不到等效的启动函数的原因logging.shutdown()
是在导入logging
模块时启动/初始化了日志系统。除其他外,它实例化隐式根记录器并通过atexit注册logging.shutdown
为退出处理程序。 后者是无需显式调用上述解决方案的原因。Python 解释器将在准备因退出处理程序注册而关闭解释器时在完成期间调用它。然后遍历已注册处理程序的列表并调用它们的方法,这将在干净关闭期间执行日志归档。logging.shutdown()
logging.shutdown()
close()
根据您选择的移动(和重命名)旧日志文件的方法,上述解决方案可能需要一些额外的异常保护措施。os.rename
如果目标路径已经存在,即当您已经在同一天停止并启动您的进程时,将引发异常,同时os.replace
会静默覆盖现有文件。
因此,我建议不仅按当前日期而且按时间命名归档日志。
在上面,将当前日期添加到存档文件名是通过datetime
's完成的strftime
,因此 'log_ %Y%m%d ' 作为archive_name
自定义文件处理程序参数的默认值。前面的字符%
是有效的格式代码,替换为调用它的对象strftime()
的相应部分。datetime
要将当前时间附加到存档日志文件名,您只需将相应的格式代码附加到archive_name
,例如:'log_%Y%m%d_ %H%M%S ',这将导致日志名称,例如log_20200819_123721
.
添加回答
举报