您的位置:首页 > 编程语言 > Python开发

python logging 实现的进程安全的文件回滚日志类

2017-02-20 13:52 686 查看
  python标准库中的logging模块在记录日志时经常会用到,但在实际使用发现它自带的用于本地日志回滚的类

logging.handlers.RotatingFileHandler 在多进程环境下会出现不同进程向不同文件写的问题,原因就是在当前

日志文件写满后回滚的时候没有处理好并发问题(或者可以说基本没处理),因此自己实现了一个相似功能的类,

本来是打算使用多进程锁,写完后发现没啥用。。。,于是就新建了一个.lock文件作为锁来处理多进程。用文件的

修改时间来控制只能有一个进程访问。

  代码如下:

#coding=utf-8
import os
import sys
import json
import time
import logging
import traceback
import logging.handlers
from multiprocessing import Lock

class SpiderRotatingFileHandler(logging.handlers.RotatingFileHandler):
u'''
文件回滚日志处理器
特点:
1. 利用备份文件修改时间做判断 修复了多进程下同时多个日志文件被写入的bug
2. 可选项 使用json格式记录日志文件

'''
def __init__(self, filename, mode='a', maxBytes=0,
backupCount=0, encoding=None, delay=0, is_json=False):
logging.handlers.RotatingFileHandler.__init__(self,
filename, mode, maxBytes, backupCount, encoding, delay)
# 格式处理器
self.Formatter = logging.Formatter()
# 进程锁
self.my_lock = Lock()

self.is_json = is_json
if self.is_json:
self.format = self.json_format

def json_format(self, record):
u'''
json 格式化日志
@record: 日志记录对象
type: logging.LogRecord
'''
# 增加 asctime 属性
record.asctime = self.Formatter.formatTime(record)
#
message = record.getMessage()
log_data = {}
# 检查是否为json格式 并且是字典形式
try:
log_data = json.loads(message)
if not isinstance(log_data, dict):
log_data = {}
except Exception as e:
exc_info = traceback.format_exc()
#sys.stderr.write(exc_info)

# 获取日志基本信息
log_record_basic_fields = [
"levelname", "filename", "lineno",
"name", "created", "asctime",
]

if not log_data:
log_data.update({
"_message": message,
})

for attr in log_record_basic_fields:
value = getattr(record, attr, "")
log_data.update({
"_{}".format(attr): value,
})
try:
result = json.dumps(log_data, ensure_ascii=False)
except:
result = json.dumps(log_data)
return result

def doRollover(self):
"""
Do a rollover, as described in __init__().
"""
with self.my_lock:
if self.stream:
self.stream.close()
self.stream = None
lock_file = "%s.lock"%self.baseFilename
max_modify_interval = 3 # seconds
do_flag = 0

# 利用 Lock 文件被修改时间保证不会出现同时多个文件被写入
if not os.path.exists(lock_file):
with open(lock_file, "w"):
pass
do_flag = 1
elif time.time() - os.stat(lock_file).st_mtime > max_modify_interval:
do_flag = 1
else:
pass
if do_flag:
for i in range(self.backupCount - 1, 0, -1):
sfn = "%s.%d" % (self.baseFilename, i)
dfn = "%s.%d" % (self.baseFilename, i + 1)
if os.path.exists(sfn):
# 删除最大备份文件
if os.path.exists(dfn):
os.remove(dfn)
os.rename(sfn, dfn)

dfn = self.baseFilename + ".1"
if os.path.exists(dfn):
os.remove(dfn)

if os.path.exists(self.baseFilename):
os.rename(self.baseFilename, dfn)
# 刷新 Lock 文件修改时间
with open(lock_file, "w"):
pass

if not self.delay:
self.stream = self._open()
return


  经过测试后发现,日志文件不再出现混乱写入(不过总感觉 3 秒好像还会出现点问题,万一在3秒内写满了日

志文件可能会造成日志文件大小超过限制。)

  json格式的日志输出算是附加的功能吧

  ok,欢迎找茬
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: