Python logger模块

1 logging 模块简介

logging 模块是 Python 内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比 print,具备如下优点:

  1. 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息;
  2. print 将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging 则可以由开发者决定将信息输出到什么地方,以及怎么输出;
 Logger 从来不直接实例化,经常通过 logging 模块级方法(Module-Level  Function)logging.getLogger(name) 来获得,其中如果 name 不给定就用 root。名字是以点号分割的命名方式命名的 (a.b.c)。对同一个名字的多个调用 logging.getLogger() 方法会返回同一个 logger 对象。这种命名方式里面,后面的 loggers 是前面 logger 的子 logger,自动继承父 loggers 的 log 信息,正因为此, 没有必要把一个应用的所有 logger 都配置一遍,只要把顶层的 logger 配置好了,然后子 logger 根据需要继承就行了。
   logging.Logger 对象扮演了三重角色:
       首先, 它暴露给应用几个方法以便应用可以在运行时写 log.
       其次,Logger 对象按照 log 信息的严重程度或者根据 filter 对象来决定如何处理 log 信息 (默认的过滤功能).
       最后,logger 还负责把 log 信息传送给相关的 handlers.

2 logging 模块使用

2.1 基本使用

配置 logging 基本的设置,然后在控制台输出日志,

import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
 
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

运行时,控制台输出,

1 2016-10-09 19:11:19,434 - __main__ - INFO - Start print log
2 2016-10-09 19:11:19,434 - __main__ - WARNING - Something maybe fail.
3 2016-10-09 19:11:19,434 - __main__ - INFO - Finish

logging 中可以选择很多消息级别,如:DEBUG,INFO,WARNING,ERROR,CRITICAL,通过赋予 logger 或者 handler 不同的级别,开发者就可以只输出错误信息到特定的记录文件,或者在调试时只记录调试信息。

 

将 logger 的级别改为 DEBUG,再观察一下输出结果

1
logging.basicConfig(level = logging.DEBUG,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

从输出结果可以看到,输出了 debug 的日志记录

1
2
3
4
2016-10-09 19:12:08,289 - __main__ - INFO - Start print log
2016-10-09 19:12:08,289 - __main__ - DEBUG - Do something
2016-10-09 19:12:08,289 - __main__ - WARNING - Something maybe fail.
2016-10-09 19:12:08,289 - __main__ - INFO - Finish
1
logging.basicConfig函数各参数:
1
2
3
4
5
6
7
8
9
10
11
filename:指定日志文件名;
 
filemode:和file函数意义相同,指定日志文件的打开模式,'w'或者'a'
 
format:指定输出的格式和内容,format可以输出很多有用的信息,
 
datefmt:指定时间格式,同time.strftime();
 
level:设置日志级别,默认为logging.WARNNING;
 
stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略;  
Formatters 定义了 Logger 记录的输出格式。
     定义了最终 log 信息的内容格式,应用可以直接实例化 Foamatter 类。信息格式字符串用 %(<dictionary key>)s 风格的字符串做替换。
属性名称
    格式  
                                       说明  
name
%(name)s
日志的名称
asctime
%(asctime)s
可读时间,默认格式‘2003-07-08 16:49:45,896’,逗号之后是毫秒
filename
%(filename)s
文件名,pathname 的一部分
pathname
%(pathname)s
文件的全路径名称
funcName
%(funcName)s
调用日志多对应的方法名
levelname
%(levelname)s
日志的等级
levelno
%(levelno)s
数字化的日志等级
lineno
%(lineno)d
被记录日志在源码中的行数
module
%(module)s
模块名
msecs %(msecs)d 时间中的毫秒部分
process
%(process)d
进程的 ID
processName
%(processName)s
进程的名称
thread
%(thread)d
线程的 ID
threadName
%(threadName)s
线程的名称
relativeCreated
%(relativeCreated)d
日志被创建的相对时间,以毫秒为单位

2.2 将日志写入到文件

2.2.1 将日志写入到文件

设置 logging,创建一个 FileHandler,并对输出消息的格式进行设置,将其添加到 logger,然后将日志写入到指定的文件中,

1
2
3
4
5
6
7
8
9
10
11
12
13
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
 
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

 log.txt 中日志数据为:

2017-07-25 15:02:09,905 - __main__ - INFO - Start print log
2017-07-25 15:02:09,905 - __main__ - WARNING - Something maybe fail.
2017-07-25 15:02:09,905 - __main__ - INFO - Finish

2.2.2 将日志同时输出到屏幕和日志文件

logger 中添加 StreamHandler,可以将日志输出到屏幕上,

 可以在 log.txt 文件和控制台中看到

2017-07-25 15:03:05,075 - __main__ - INFO - Start print log
2017-07-25 15:03:05,075 - __main__ - WARNING - Something maybe fail.
2017-07-25 15:03:05,075 - __main__ - INFO - Finish

可以发现,logging 有一个日志处理的主对象,其他处理方式都是通过 addHandler 添加进去,logging 中包含的 handler 主要有如下几种,

 

2.2.3 日志回滚

使用 RotatingFileHandler,可以实现日志回滚,

 可以在工程目录中看到,备份的日志文件,

.3 设置消息的等级

可以设置不同的日志等级,用于控制日志的输出

1
2
3
4
5
6
7
8
日志等级:使用范围
 
FATAL:致命错误
CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
ERROR:发生错误时,如IO操作失败或者连接问题
WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
INFO:处理请求或者状态变化等日常事务
DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态

 setLevel(lvl)      定义处理 log 的最低等级,内建的级别为:DEBUG,INFO,WARNING,ERROR,CRITICAL;下图是级别对应数值
              

2.4 捕获 traceback

Python 中的 traceback 模块被用于跟踪异常返回信息,可以在 logging 中记录下 traceback

 控制台和日志文件 log.txt 中输出

1 2017-07-25 15:04:24,045 - __main__ - INFO - Start print log
2 2017-07-25 15:04:24,045 - __main__ - WARNING - Something maybe fail.
3 2017-07-25 15:04:24,046 - __main__ - ERROR - Faild to open sklearn.txt from logger.error
4 Traceback (most recent call last):
5   File "E:\PYTHON\Eclipse\eclipse\Doc\14day5\Logger 模块 \Logging.py", line 71, in <module>
6     open("sklearn.txt","rb")
7 IOError: [Errno 2] No such file or directory: 'sklearn.txt'
8 2017-07-25 15:04:24,049 - __main__ - INFO - Finish
View Code

 

也可以使用 logger.exception(msg,_args),它等价于 logger.error(msg,exc_info = True,_args),

1
2
3
4
logger.error("Faild to open sklearn.txt from logger.error",exc_info = True)
替换为,
logger.exception("Failed to open sklearn.txt from logger.exception")

 

2.5 多模块使用 logging

主模块 mainModule.py

  子模块 subModule.py

 执行之后,在控制和日志文件 log.txt 中输出

1 2017-07-25 15:05:07,427 - mainModule - INFO - creating an instance of subModule.subModuleClass
2 2017-07-25 15:05:07,427 - mainModule.sub.module - INFO - creating an instance in SubModuleClass
3 2017-07-25 15:05:07,427 - mainModule - INFO - calling subModule.subModuleClass.doSomething
4 2017-07-25 15:05:07,427 - mainModule.sub.module - INFO - do something in SubModule
5 2017-07-25 15:05:07,427 - mainModule.sub.module - INFO - finish something in SubModuleClass
6 2017-07-25 15:05:07,427 - mainModule - INFO - done with  subModule.subModuleClass.doSomething
7 2017-07-25 15:05:07,427 - mainModule - INFO - calling subModule.some_function
8 2017-07-25 15:05:07,427 - mainModule.sub - INFO - call function some_function
9 2017-07-25 15:05:07,428 - mainModule - INFO - done with subModule.some_function
View Code

 

说明:

首先在主模块定义了 logger'mainModule',并对它进行了配置,就可以在解释器进程里面的其他地方通过 getLogger('mainModule') 得到的对象都是一样的,不需要重新配置,可以直接使用。定义的该 logger 的子 logger,都可以共享父 logger 的定义和配置,所谓的父子 logger 是通过命名来识别,任意以 'mainModule' 开头的 logger 都是它的子 logger,例如 'mainModule.sub'。

实际开发一个 application,首先可以通过 logging 配置文件编写好这个 application 所对应的配置,可以生成一个根 logger,如 'PythonAPP',然后在主函数中通过 fileConfig 加载 logging 配置,接着在 application 的其他地方、不同的模块中,可以使用根 logger 的子 logger,如 'PythonAPP.Core','PythonAPP.Web' 来进行 log,而不需要反复的定义和配置各个模块的 logger。

 

3 通过 JSON 或者 YAML 文件配置 logging 模块

尽管可以在 Python 代码中配置 logging,但是这样并不够灵活,最好的方法是使用一个配置文件来配置。在 Python 2.7 及以后的版本中,可以从字典中加载 logging 配置,也就意味着可以通过 JSON 或者 YAML 文件加载日志的配置。

3.1 通过 JSON 文件配置

JSON 配置文件

  通过 JSON 加载配置文件,然后通过 logging.dictConfig 配置 logging,

  

3.2 通过 YAML 文件配置

通过 YAML 文件进行配置,比 JSON 看起来更加简介明了,

  通过 YAML 加载配置文件,然后通过 logging.dictConfig 配置 logging

  

4 Reference

http://wjdadi-gmail-com.iteye.com/blog/1984354

关于 logging 的一些琐事

python logging 重复写日志问题

 

本文摘自:http://www.cnblogs.com/zhbzz2007/p/5943685.html