在项目中定制python的logging模块的实例

在一个项目中,日志模块是必不可少的,健壮的日志输出有助于及时发现问题和调试。python的日志模块logging为我们提供了强大的日志功能。

一.logging模块简介

我们先看一个标准的程序:

import logging
logger=logging.getLogger()
handler=logging.FileHandler("Log_test.txt")
logger.addHandler(handler)
logger.setLevel(logging.NOTSET)
logger.error("This is an error message")
logger.info("This is an info message")
logger.critical("This is a critical message")
 

这个程序应用了logging中常用的filehandler,在日志文件中会出现三行内容:

This is an error message

This is an info message

This is a critical message

上面程序的第2行是生成一个日志对象,里面的参数时日志的名字,可以带,也可以不带。第3行是生成了一个handler,logging支持很多种 Handler,像FileHandler,SocketHandler等待,这里由于我们要写文件,所以用了FileHandler,它的参数就是 filename,默认当前路径,当然我们可以自己指定路径。

第5行设置日志信息输出的级别。Logging提供了多种日志级别,如 NOTSET,DEBUG,INFO,WARNING,ERROR,CRITICAL等,每个级别都对应一个数值,如果我们不自己设置输出级别,那么系统 会执行缺省级别,值为30,就warning。当写入日志时,小于指定级别的信息将被忽略。因此为了输出想要的日志级别一定要设置好此参数。这里我设为NOTSET(值为0),也就是想输出所有信息。

 

关于logging模块的详细介绍,以及程序实例,可以参考这个链接

http://www.red-dove.com/python_logging.html

 

二.实际项目中的应用

目前我做的一个爬虫项目,程序由计划任务定时执行,程序一旦跑起来就不会天天去关注,所以对日志模块就以下四个要求。

1.详细的debug信息写入文件,一旦程序出错,可以在文件里面找到详细的出错信息

2.控制台实时打印重要信息,如一些error和warning

3.由于程序属于无人监管状态,当一般错误信息达到一定数量后,自动发邮件提醒。

4.出现重大的错误后,单独发邮件提醒,并挂起程序。如数据库死掉,磁盘阵列无法访问等。

 

综合以上的要求,我需要用到logging中得FileHandler(由于日志量会很大,这里需要用到RotatingFileHandler,日志达到设定大小后自动写到另外的文件中),Streamhandler(往控制台输出日志),SMTPHanler(用于致命错误的邮件提醒), MemoryHandler(用于缓存一般错误日志,达到阀值之后自动邮件提醒).

logging的配置如下:

#encoding=utf-8
import logging
import time
import smtplib
from email.mime.text import MIMEText
import logging.handlers
import os
#日志文件的路径,FileHandler不能创建目录,这里先检查目录是否存在,不存在创建他
#当然也可以继承之后重写FileHandler的构造函数
LOG_FILE_PATH="log/Execution.log"
dir= os.path.dirname(LOG_FILE_PATH)
if not os.path.isdir(dir):
    os.mkdir(dir)
#写入文件的日志等级,由于是详细信息,推荐设为debug
FILE_LOG_LEVEL="DEBUG"
#控制台的日照等级,info和warning都可以,可以按实际要求定制
CONSOLE_LOG_LEVEL="INFO"
#缓存日志等级,最好设为error或者critical
MEMOEY_LOG_LEVEL="ERROR"
#致命错误等级 
URGENT_LOG_LEVEL="CRITICAL"
#缓存溢出后的邮件标题
ERROR_THRESHOLD_ACHEIVED_MAIL_SUBJECT="Too many errors occurred during the execution"
#缓存溢出的阀值
ERROR_MESSAGE_THRESHOLD=50
#致命错误发生后的邮件标题
CRITICAL_ERROR_ACHEIVED_MAIL_SUBJECT="Fatal error occurred"

#邮件服务器配置
MAIL_HOST="your exchange server"
FROM="from"
MAIL_TO=["address1","address2"]

class OptmizedMemoryHandler(logging.handlers.MemoryHandler):
    """
       由于自带的MemoryHandler达到阀值后,每一条缓存信息会单独处理一次,这样如果阀值设的100,
      会发出100封邮件,这不是我们希望看到的,所以这里重写了memoryHandler的2个方法,
      当达到阀值后,把缓存的错误信息通过一封邮件发出去.
    """
    def __init__(self, capacity,mail_subject):
        logging.handlers.MemoryHandler.__init__(self, capacity,flushLevel=logging.ERROR, target=None)
        self.mail_subject=mail_subject
        self.flushed_buffers=[]
    def shouldFlush(self, record):
        """
        检查是否溢出
        """
        if len(self.buffer) >= self.capacity:
            return True
        else:
            return False
    def flush(self):
        """
         缓存溢出时的操作,
        1.发送邮件 2.清空缓存 3.把溢出的缓存存到另一个列表中,方便程序结束的时候读取所有错误并生成报告
        """
        if self.buffer!=[] and len(self.buffer) >= self.capacity:
            content=""
            for record in self.buffer:
                message= record.getMessage()
                level= record.levelname
                ctime= record.created
                t=time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(ctime))
                content+=t+" "+"*"+level+"* : "+message+"\n"
            self.mailNotification(self.mail_subject, content)
            self.flushed_buffers.extend(self.buffer)
            self.buffer = []
    def mailNotification(self,subject,content):
        """
                发邮件的方法
        """
        msg=MIMEText(content)
        msg['Subject']=subject
        msg['From']=FROM
        msg['To']=";".join(MAIL_TO)
        try:
            s=smtplib.SMTP()
            s.connect(MAIL_HOST)
            s.sendmail(FROM,MAIL_TO,msg.as_string())
            s.close()
        except Exception,e:
            self.logger.error(str(e))

            
MAPPING={"CRITICAL" :50,
           "ERROR" : 40,
           "WARNING" : 30,
           "INFO" : 20,
           "DEBUG" : 10,
           "NOTSET" :0,
           }

class logger:
    """
    logger的配置
    """
    def __init__(self,logFile,file_level,console_level,memory_level,urgent_level):

        self.config(logFile, file_level, console_level, memory_level,urgent_level)
    def config(self,logFile,file_level,console_level,memory_level,urgent_level):
        #生成root logger
        self.logger = logging.getLogger("crawler")
        self.logger.setLevel(MAPPING[file_level])
        #生成RotatingFileHandler,设置文件大小为10M,编码为utf-8,最大文件个数为100个,如果日志文件超过100,则会覆盖最早的日志
        self.fh = logging.handlers.RotatingFileHandler(logFile,mode='a', maxBytes=1024*1024*10, backupCount=100, encoding="utf-8")
        self.fh.setLevel(MAPPING[file_level])
        #生成StreamHandler
        self.ch = logging.StreamHandler()
        self.ch.setLevel(MAPPING[console_level])  
        #生成优化过的MemoryHandler,ERROR_MESSAGE_THRESHOLD是错误日志条数的阀值
        self.mh = OptmizedMemoryHandler(ERROR_MESSAGE_THRESHOLD,ERROR_THRESHOLD_ACHEIVED_MAIL_SUBJECT)
        self.mh.setLevel(MAPPING[memory_level])
        #生成SMTPHandler
        self.sh=logging.handlers.SMTPHandler(MAIL_HOST,FROM,";".join(MAIL_TO),CRITICAL_ERROR_ACHEIVED_MAIL_SUBJECT)
        self.sh.setLevel(MAPPING[urgent_level])
        #设置格式
        formatter = logging.Formatter("%(asctime)s *%(levelname)s* : %(message)s",'%Y-%m-%d %H:%M:%S')  
        self.ch.setFormatter(formatter)  
        self.fh.setFormatter(formatter)
        self.mh.setFormatter(formatter)
        self.sh.setFormatter(formatter)
        #把所有的handler添加到root logger中
        self.logger.addHandler(self.ch)  
        self.logger.addHandler(self.fh)
        self.logger.addHandler(self.mh)
        self.logger.addHandler(self.sh)

    def debug(self,msg):
        if msg is not None:
            self.logger.debug(msg)
    def info(self,msg):
        if msg is not None:
            self.logger.info(msg)
    def warning(self,msg):
        if msg is not None:
            self.logger.warning(msg)
    def error(self,msg):
        if msg is not None:
            self.logger.error(msg)
    def critical(self,msg):
        if msg is not None:
            self.logger.critical(msg)
LOG=logger(LOG_FILE_PATH,FILE_LOG_LEVEL,CONSOLE_LOG_LEVEL,MEMOEY_LOG_LEVEL,URGENT_LOG_LEVEL)
if __name__=="__main__":
    #测试代码
    for i in range(50):
        LOG.error(i)
        LOG.debug(i)
    LOG.critical("Database has gone away")

在主程序中,只需要把LOG对象import进去,就可以通过LOG提供的info,debug。。。的方法输入日志。

当输入一条debug日志,由于等级是最低的,只有RotatingFileHandler的日志等级与之匹配,所以文件中会记录这条debug信息。

当输入一条error日志时,匹配日志等级,文件中会记录这条日志,控制台中会打印出来,MemoryHandler模块会缓存这条日志,并判断是否达到溢出条件。

当输入一条critical日志时,匹配日志等级,文件中会记录这条日志,控制台中会打印出来,MemoryHandler模块会缓存这条日志,并判断是否达到溢出条件,同时SMTPHandler会直接发送致命错误邮件提醒。

 

使用了这个日志配置之后,我们就可以高枕无忧的干其他事情了,只要没有邮件提醒,程序都在正常运行。。

一旦收到邮件后,及时查看文件日志,也不难找出错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值