文章目录
一、按日志大小分割
1、使用RotatingFileHandler 日志日志处理器
- 需配置maxBytes与backupCount参数,不为0时生效
说明:log文件大小接近maxBytes时,新建一个文件作为log的输出,旧的文件会被加上类似’.1’、’.2’的后缀。 举个例子,如果backupCount=5,log file定义的名字为app.log,你会得到app.log, app.log.1, app.log.2 一直到 app.log.5。 然而被写入日志的永远是app.log,写满了之后重命名为app.log.1,如果app.log.1存在,app.log.1会先被重名名为app.log.2,依此类推。 另外,如果app.log.5存在,它会被删除。
# settings配置
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'default': { # 用于文件输出
'level': 'INFO', # 输出日志等级
'class': 'logging.handlers.RotatingFileHandler', # 日志类型
'filename': '%s/django.log' % LOGGING_DIR, # 具体日志文件的名字
'maxBytes': 1024 * 1024 * 300, # 日志大小 300M
'backupCount': 5, # 备份数量 5个
'formatter':'standard', # 输出日志格式
'encoding': 'utf-8', # 输出日志编码
}
二、按日期分割
1、通过TimedRotatingFileHandler 日志处理器实现
'file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': '%s/django.log' % LOGGING_DIR, # 日志的文件名
# TimedRotatingFileHandler的参数
# 目前设定每天一个日志文件
# 'S' | 秒
# 'M' | 分
# 'H' | 时
# 'D' | 天
# 'W0'-'W6' | 周一至周日
# 'midnight' | 每天的凌晨
'when': 'midnight', # 间间隔的类型
'interval': 1, # 时间间隔
'backupCount': 100, # 能留几个日志文件;过数量就会丢弃掉老的日志文件
'formatter': 'standard', # 日志文本格式
'encoding': 'utf-8', # 日志文本编码
},
三、多进程情况下补充
RotatingFileHandler 和 TimedRotatingFileHandler在多进程环境下会出现异常
1)异常情况如下:
- 日志写入错乱;
- 日志并没有按天分割,而且还会丢失。
2)出现异常的原因为:
- Django logging 是基于 Python logging 模块实现的,logging 模块是线程安全的,但不能保证多进程安全。
- 多进程日志操作时,句柄操作可能混乱;一个进程在做读写操作时,文件可能被另一进程执行删除重名操作
解决方案:
1、使用concurrent-log-handler 三方包中的ConcurrentRotatingFileHandler处理器
- pip install concurrent-log-handler
# settings.py
LOGGING = {
...
'handlers': {
...
'file': {
'level': 'INFO',
'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',
'filename': os.path.join(LOGS_DIR, 'app.log'),
'formatter': 'verbose',
'maxBytes': 1024,
'backupCount': 5
},
...
}
...
}
缺点:
- 仅支持文件大小分割,不支持日期分割
2、重写logging.handlers.TimedRotatingFileHandler实现多进程日期分割
重写原理:
- 去掉删文件的逻辑
- 在切割文件时,及时将写入句柄更新到最新。
import os
import time
from logging.handlers import TimedRotatingFileHandler
class CommonTimedRotatingFileHandler(TimedRotatingFileHandler):
@property
def dfn(self):
currentTime = int(time.time())
# get the time that this sequence started at and make it a TimeTuple
dstNow = time.localtime(currentTime)[-1]
t = self.rolloverAt - self.interval
if self.utc:
timeTuple = time.gmtime(t)
else:
timeTuple = time.localtime(t)
dstThen = timeTuple[-1]
if dstNow != dstThen:
if dstNow:
addend = 3600
else:
addend = -3600
timeTuple = time.localtime(t + addend)
dfn = self.rotation_filename(self.baseFilename + "." + time.strftime(self.suffix, timeTuple))
return dfn
def shouldRollover(self, record):
"""
是否应该执行日志滚动操作:
1、存档文件已存在时,执行滚动操作
2、当前时间 >= 滚动时间点时,执行滚动操作
"""
dfn = self.dfn
t = int(time.time())
if t >= self.rolloverAt or os.path.exists(dfn):
return 1
return 0
def doRollover(self):
"""
执行滚动操作
1、文件句柄更新
2、存在文件处理
3、备份数处理
4、下次滚动时间点更新
"""
if self.stream:
self.stream.close()
self.stream = None
# get the time that this sequence started at and make it a TimeTuple
dfn = self.dfn
# 存档log 已存在处理
if not os.path.exists(dfn):
self.rotate(self.baseFilename, dfn)
# 备份数控制
if self.backupCount > 0:
for s in self.getFilesToDelete():
os.remove(s)
# 延迟处理
if not self.delay:
self.stream = self._open()
# 更新滚动时间点
currentTime = int(time.time())
newRolloverAt = self.computeRollover(currentTime)
while newRolloverAt <= currentTime:
newRolloverAt = newRolloverAt + self.interval
# If DST changes and midnight or weekly rollover, adjust for this.
if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
dstAtRollover = time.localtime(newRolloverAt)[-1]
dstNow = time.localtime(currentTime)[-1]
if dstNow != dstAtRollover:
if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
addend = -3600
else: # DST bows out before next rollover, so we need to add an hour
addend = 3600
newRolloverAt += addend
self.rolloverAt = newRolloverAt
声明:本文引用自https://blog.csdn.net/TFATS/article/details/106408296 文章写的非常好,非常清晰