logging模块

很多应用程序都需要记录日志,日志也是软件运行的很重要的方面.Python的标准日志模块可以与其他功能有效的集成,这样就不用自行编写日志程序,只要把需要记录的内容交给日志记录器即可.日志还需要一定程度的个性化,标准库的日志模块就可以和其他python程序有效的集成,自定义格式和调用接口,为应用程序建立一个logging实例,就可以使用日志功能了.其实logging模块的功能就是一个记录者,以规定好的模式记录程序传递给它的信息,下边就看来如何创建和使用这样一个记录者.这里有一个简要的说明.

模块方法操作logging:

导入logging模块.日志一般分级别,所谓级别也就是严重程度,日志模块首先能做的就是按照安全级别显示日志.
默认的日志级别是CRITICAL > ERROR > WARNING > INFO > DEBUG

import logging

logging.debug('debug message')   #显示各个级别的日志消息
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')   

执行之后,python默认会显示warning及更严重级别的日志,说明默认显示级别是warning.而且python是将日志输出到标准输出,也就是屏幕上.后边通过logging.basicconfig()方法来修改设置.
打印出来的信息第一个冒号之前是级别,冒号之后是默认root用户,之后冒号后边是信息.

logging.basicConfig(level=logging.DEBUG,  
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
                    datefmt='%a, %d %b %Y %H:%M:%S',  
                    filename='/tmp/test.log',  
                    filemode='w')  

basicConfig方法通过传入关键字参数的方法修改logging的行为.主要关键字参数有:

filename 指定日志输出的文件,如果指定了filenmae,则stream参数会失效
filemode 文件写模式,默认为a,即追加.设置为w则是覆盖
format 用$(特定字符)s来表示写入日志的格式,见下边的列表
datefmt 指定时间格式,与time模块里的时间格式类似,一般采用%Y-%m-%d %H:%M:%S
style 指定采用何种format形式,参数可以为’%’, ‘{‘, ‘$’,分别表示C风格,python的字符串.format风格以及$风格
level 设置默认级别,参数为logging.全大写级别
stream 指定输出流,可以指定sys.stderr,sys.stdout或者文件对象,默认为sys.stderr.若给出了filename,该参数被忽略.

其中format直接格式化了写入日志的内容,这里的格式很重要:

%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用时间戳浮点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s 字符串形式的当前时间
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 输入的日志消息

其中%(message)s就是文章最开始通过 logging.debug(string)及其他按照日志级别传进来的消息.
但是如果每次这样一个个传日志消息,会很麻烦,而且配置不够灵活(比如不能同时向屏幕输出和写入文件),除了模块方法以外,最好还是使用logging模块里的logger对象实例化来进行操作.

logger对象操作

# 实例化logger对象,就是一个'记录者对象'
logger=logging.getLogger('logname')  # 如果不传参数,拿到的就是root用户实例,如果指定logname,则这个logname是root用户的一个子实例.相同名字的logname,就是同一个实例.
# 实例化logger的文件处理对象
fh = logging.FileHandler('test.log') # 指定文件名
# 实例化标准输出对象
ch = logging.StreamHandler()         # 标准输出参数为空
# 实例化格式对象
fm = logging.Formatter('%(asctime)s %(message)s')  # 格式的参数按照上边模块方法里的format格式使用

logger.addHandler(fh)  #给logger加上文件输出功能,如果不添加,没有输出文件的功能
logger.addHandler(ch)  #给logger加上标准输出功能,如果不添加,没有输出到屏幕的功能

fh.setFormatter(fm)    #给文件输出功能加上格式,如果不做这步,输出文件的格式按照默认
ch.setFormatter(fm)    #给标准输出功能加上格式,如果不做这步,在屏幕上显示的格式按照默认格式

logger.setLevel('DEBUG')

# 最后用logger对象打印信息
logger.debug('this is debug level message')
logger.info('this is info level message')
logger.warning('this is warning level message')
logger.error('this is error level message')
logger.critical('this is critical level message')

可以将生成logger的过程写在一个函数里,以后需要创建日志的时候,就可以通过这个函数返回一个日志记录对象,然后向日志记录对象内写入信息即可.
父子对象重复打印的问题:如果父对象没有输出功能的话,则不会重复输出.如果父对象有输出机制,则会重复输出一次,有几层父对象就会输出几次.