python logger 并发场景实现日志切割和日志自动删除功能

实现原理:
重写WatchedFileHandler实现

自带的RotatingFileHandler模块只适用于单进程多进程中会导致日志丢失

logger配置

仅将file_path改为日志存储的目录地址

 

 



自定义日志类

  1 # -*-coding:utf-8-*-
  2 import datetime
  3 import os
  4 from stat import ST_DEV, ST_INO
  5 from logging.handlers import WatchedFileHandler, RotatingFileHandler
  6 
  7 
  8 class CommonTimedRotatingFileHandler(WatchedFileHandler):
  9 
 10     def __init__(self, file_path, mode='a', encoding=None, delay=0):
 11         if not os.path.exists(file_path):
 12             os.makedirs(file_path)
 13         self.file_path = file_path
 14 
 15         self.file_name = "{}.log".format(datetime.datetime.now().strftime("%Y-%m-%d"))
 16         file_name = os.path.join(file_path, self.file_name)
 17 
 18         super(CommonTimedRotatingFileHandler, self).__init__(file_name,  mode=mode, encoding=encoding, delay=delay)
 19 
 20     def reopenIfNeeded(self):
 21         """
 22         Reopen log file if needed.
 23 
 24         Checks if the underlying file has changed, and if it
 25         has, close the old stream and reopen the file to get the
 26         current stream.
 27         """
 28         # Reduce the chance of race conditions by stat'ing by path only
 29         # once and then fstat'ing our new fd if we opened a new log stream.
 30         # See issue #14632: Thanks to John Mulligan for the problem report
 31         # and patch.
 32         try:
 33             # stat the file by path, checking for existence
 34             sres = os.stat(self.baseFilename)
 35         except OSError as e:
 36             sres = None
 37         # compare file system stat with that of our stream file handle
 38         if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
 39             if self.stream is not None:
 40                 # we have an open file handle, clean it up
 41                 self.stream.flush()
 42                 self.stream.close()
 43                 self.stream = None  # See Issue #21742: _open () might fail.
 44                 # open a new file handle and get new stat info from that fd
 45                 self.stream = self._open()
 46                 self._statstream()
 47 
 48     def getFilesToDelete(self):
 49         """
 50         Determine the files to delete when rolling over.
 51 
 52         More specific than the earlier method, which just used glob.glob().
 53         """
 54         dirName, baseName = os.path.split(self.baseFilename)
 55         fileNames = os.listdir(dirName)
 56         result = []
 57         prefix = baseName + "."
 58         plen = len(prefix)
 59         for fileName in fileNames:
 60             if fileName[:plen] == prefix:
 61                 suffix = fileName[plen:]
 62                 if self.extMatch.match(suffix):
 63                     result.append(os.path.join(dirName, fileName))
 64         result.sort()
 65         if len(result) < self.backupCount:
 66             result = []
 67         else:
 68             result = result[:len(result) - self.backupCount]
 69         return result
 70 
 71     def emit(self, record):
 72         # 检查日志数量是不是超过限制如果超过限制自动删除
 73         fileNames = filter(lambda x: x[0] != ".", os.listdir(self.file_path))
 74         if len(fileNames) > 7:
 75             fileNames.sort()
 76             for item in fileNames[:-7]:
 77                 temp_file_path = os.path.join(self.file_path, item)
 78                 try:
 79                     # 并发执行时文件可能会被其他进程已删除,因此需要过滤异常情况
 80                     if os.path.exists(temp_file_path):
 81                         os.remove(temp_file_path)
 82                 except OSError as e:
 83                     pass
 84 
 85         # 根据当前日期创建文件
 86         cur_datetime = datetime.datetime.now()
 87         current_file_name = "{}.log".format(cur_datetime.strftime("%Y-%m-%d"))
 88 
 89         # 文件名不一致:新建文件+重新打开+重新读取os.state
 90         if self.file_name != current_file_name:
 91             self.file_name = current_file_name
 92             # 重新赋值,当前文件名应该是最新日期
 93             self.baseFilename = os.path.abspath(os.path.join(self.file_path, current_file_name))
 94 
 95             if self.stream:
 96                 self.stream.flush()
 97                 self.stream.close()
 98             self.stream = self._open()
 99             self._statstream()
100 
101         # 检查文件是否已更改。 如果已更改,则会刷新并关闭现有流然后重新打开文件
102         # 该方法在python3中已实现,python2中需要自己实现
103         self.reopenIfNeeded()
104         super(CommonTimedRotatingFileHandler, self).emit(record)

 




posted @ 2023-03-02 21:08  你看起来真的很好吃  阅读(257)  评论(0编辑  收藏  举报