很多应用程序都需要记录日志,日志也是软件运行的很重要的方面.Python的标准日志模块可以与其他功能有效的集成,这样就不用自行编写日志程序,只要把需要记录的内容交给日志记录器即可.日志还需要一定程度的个性化,标准库的日志模块就可以和其他python程序有效的集成,自定义格式和调用接口,为应用程序建立一个logging实例,就可以使用日志功能了.其实logging模块的功能就是一个记录者,以规定好的模式记录程序传递给它的信息,下边就看来如何创建和使用这样一个记录者.这里有一个简要的说明.
模块方法操作logging:
导入logging模块.日志一般分级别,所谓级别也就是严重程度,日志模块首先能做的就是按照安全级别显示日志. 默认的日志级别是CRITICAL > ERROR > WARNING > INFO > DEBUGimport 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对象操作
```Python # 实例化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的过程写在一个函数里,以后需要创建日志的时候,就可以通过这个函数返回一个日志记录对象,然后向日志记录对象内写入信息即可.
父子对象重复打印的问题:如果父对象没有输出功能的话,则不会重复输出.如果父对象有输出机制,则会重复输出一次,有几层父对象就会输出几次.