-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmultiproc_log_handler.py
125 lines (84 loc) · 3.46 KB
/
multiproc_log_handler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# -*- coding:utf-8 -*-
import datetime
import logging
import os
import re
class MultiProcLogHandler(logging.FileHandler):
def __init__(self, logfile, when="D", retain=1):
""" 初始化多进程日志切换参数设置
:参数 logfile: 指定的日志文件
:参数 when: 日志切换周期 [S, M, H, D], 默认: D
:参数 retain: 保留日志数量, 默认: 1
"""
patterns = {
"S": {"suffix": "%Y-%m-%d_%H-%M-%S", "match": r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"},
"M": {"suffix": "%Y-%m-%d_%H-%M", "match": r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"},
"H": {"suffix": "%Y-%m-%d_%H", "match": r"^\d{4}-\d{2}-\d{2}_\d{2}$"},
"D": {"suffix": "%Y-%m-%d", "match": r"^\d{4}-\d{2}-\d{2}$"},
}
self.logFile = logfile
self.roll = patterns[when.upper()]
self.logFileFormat = os.path.join("%s.%s" % (logfile, self.roll["suffix"]))
self.logFilePath = datetime.datetime.now().strftime(self.logFileFormat)
self.retain = retain
super(MultiProcLogHandler, self).__init__(self.logFilePath, "a")
def check_switch(self):
""" 检测当前写入日志是否满足切换条件
:返回: True/False
"""
currentLogFilePath = datetime.datetime.now().strftime(self.logFileFormat)
if currentLogFilePath == self.logFilePath:
return False
self.logFilePath = currentLogFilePath
return True
def switch_log(self):
""" 切换日志文件并删除不满足保留条件的日志 """
self.baseFilename = os.path.abspath(self.logFilePath)
if self.stream is not None:
self.stream.flush()
self.stream.close()
if not self.delay:
self.stream = self._open()
def get_logfile_to_delete(self):
""" 列出不满足保留条件的日志
:返回: 待删除的日志文件列表
"""
dirName, _ = os.path.split(self.baseFilename)
prefix = os.path.basename(self.logFile) + "."
plen = len(prefix)
result = []
for logFile in os.listdir(dirName):
if logFile[:plen] == prefix:
suffix = logFile[plen:]
if re.compile(self.roll["match"]).match(suffix):
result.append(os.path.join(dirName, logFile))
result.sort()
if len(result) < self.retain:
result = []
else:
result = result[:len(result) - self.retain]
return result
def emit(self, record):
""" 记录日志 切换日志 删除日志
:参数 record: 日志条目
"""
try:
if self.check_switch():
self.switch_log()
if self.retain > 0:
for s in self.get_logfile_to_delete():
os.remove(s)
logging.FileHandler.emit(self, record)
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
if __name__ == "__main__":
logHandler = MultiProcLogHandler(logfile="./test.log", when="D", retain=2)
logHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
log = logging.getLogger("test")
log.setLevel(logging.INFO)
log.addHandler(logHandler)
log.info("test info")
log.error("test error")
log.warning("test warn")