通常,我们的日志是这样写的:
_code_log_file = os.path.join(BASE_DIR, 'logs', 'code.log')
_code_log_handler = TimedRotatingFileHandler(_code_log_file, when="D")
_code_log_formatter = logging.Formatter('%(levelname)s %(pathname)s->func:%(funcName)s line:%(lineno)d %(message)s %(asctime)s')
_code_log_handler.setFormatter(_code_log_formatter)# 此处设置logger名称,否则默认的会和tornado的logger相同而使得下方设置的错误等级被轻质更新为info
code_log = logging.getLogger('code-log')
code_log.setLevel(logging.INFO)
code_log.addHandler(_code_log_handler)
这样写,一般情况下不会有问题,即使程序中使用了多线程写日志也不会导致日志丢失。(logging是线程安全的)
但是,实际项目中,为了最大量的利用CPU,我们需要启动多个python进程,这个时候,这样写日志就会出问题了。当有多个进程同时写同一个日志文件时,就会出现覆盖而导致日志信息丢失的问题。
对于这样的问题,通常有两种方案:
一. 为每个进程分配不同的文件
原理:规避了多个进程写同一个文件的情境。问题自然解决。
优点:不用写额外代码或第三方包的引用。
缺点:日志分散,不利于日志查看和监测。
二. 重写handler,用方法解决多进程写文件的问题
原理:就像同一时间多个客户买火车票的问题一样,加入锁机制。
优点:日志集中,方便查看与监测。
缺点:需要自行解决锁的相关算法或引入第三方包。
多进程写日志的第三方包ConcurrentLogHandler
详细介绍:https://pypi.python.org/pypi/ConcurrentLogHandler/0.9.1
具体使用如下:
_code_log_file = os.path.join(BASE_DIR, 'logs', 'code.log')
# 对应三个参数为:文件名,写入模式(a表示追加),文件大小(2M),最多保存5个文件
_code_log_handler = ConcurrentRotatingFileHandler(_code_log_file, "a", 2*1024*1024, 5)
_code_log_formatter = logging.Formatter('%(levelname)s %(pathname)s->func:%(funcName)s '
'line:%(lineno)d %(message)s %(asctime)s')
_code_log_handler.setFormatter(_code_log_formatter)
# 此处设置logger名称,否则默认的会和tornado的logger相同而使得下方设置的错误等级被轻质更新为info
code_log = logging.getLogger('code-log')
code_log.setLevel(logging.INFO)
code_log.addHandler(_code_log_handler)
code_log.info("balabala")
在windows上使用ConcurrentLogHandler的问题
暂不知什么原因,windows上使用该handler会出错,程序卡死在写日志的地方。调试跟踪到该Handler内部,是某个锁的问题。但该问题在正式环境Linux中不会存在,也没去深究。临时的解决方法是在日志初始化的地方判断当前系统,是windows则使用传统的日志handler。
代码如下:
import logging
from setting import BASE_DIR
import os
from cloghandler import ConcurrentRotatingFileHandler as LogHandler
import platform
import json
# 不知道为毛windows上ConcurrentRotatingFileHandler这个handler会有导致程序卡死,linux上无问题
# 暂时采用这种方法解决windows上因卡死导致的调试不方便问题
if str(platform.system()) == "Windows":
from logging.handlers import RotatingFileHandler as LogHandler
# -------------------------------运行日志(代码运行记录)-------------------------------
_code_log_file = os.path.join(BASE_DIR, 'logs', 'code.log')
_code_log_handler = LogHandler(_code_log_file, "a", 20*1024*1024, 50)
_code_log_formatter = logging.Formatter('%(levelname)s %(pathname)s->func:%(funcName)s '
'line:%(lineno)d %(message)s %(asctime)s')
_code_log_handler.setFormatter(_code_log_formatter)
# 此处设置logger名称,否则默认的会和tornado的logger相同而使得下方设置的错误等级被更新为info
code_log = logging.getLogger('code-log')
code_log.setLevel(logging.INFO)
code_log.addHandler(_code_log_handler)
更新
- 用concurrent-log-handler替换ConcurrentLogHandler可解决windows因锁机制导致的卡死问题。即pip install ConcurrentLogHandler 改为pip install concurrent-log-handler.