logging模块是Python内置的标准模块,主要用于输出运行日志,能够设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具有以下优势:html
import logging
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') # 输出为: # WARNING:root:warning message # ERROR:root:error message # CRITICAL:root:critical message
从这个例子中,咱们能够看到默认状况下python
关于logging的内容远不止这么简单,且慢慢看下去。程序员
在后面分别会说明日志级别,模块级函数记录日志,logging模块日志流处理流程服务器
日志级别分类网络
说明多线程
logging模块中提供了两种记录日志的方式:ide
说明: logging模块提供的模块级别的那些函数实际上也是经过这几个组件的相关实现类来记录日志的,只是在建立这些类的实例时设置了一些默认值。函数
上面说到了logging的basicConfig函数进行参数的配置。工具
logging.basicConfig函数各个参数测试
%(asctime)s:日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
%(created)f:日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
%(relativeCreated)d:日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干吗用的)
%(msecs)d:日志事件发生事件的毫秒部分
%(levelname)s:该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
%(levelno)s:该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
%(name)s:所使用的日志器名称,默认是'root',由于默认使用的是 rootLogger
%(message)s:日志记录的文本内容,经过 msg % args计算获得的
%(pathname)s:调用日志记录函数的源码文件的全路径
%(filename)s:pathname的文件名部分,包含文件后缀
%(module)s:filename的名称部分,不包含后缀
%(lineno)d:调用日志记录函数的源代码所在的行号
%(funcName)s:调用日志记录函数的函数名
%(process)d:进程ID
%(processName)s:进程名称,Python 3.1新增
%(thread)d:线程ID
%(thread)s:线程名称
ValueError
异常;在一开始写的那个简单的例子其实就是用模块级函数写的日志。除了上面的那种写法,还有另外一种写法。
import logging logging.log(logging.DEBUG, "This is a debug log.") logging.log(logging.INFO, "This is a info log.") logging.log(logging.WARNING, "This is a warning log.") logging.log(logging.ERROR, "This is a error log.") logging.log(logging.CRITICAL, "This is a critical log.") # 输出为: # WARNING:root:This is a warning log. # ERROR:root:This is a error log. # CRITICAL:root:This is a critical log.
正如前面一开始说的那样,日志的的默认日志级别为WARNING,只有它和它以上的日志记录被输出。而默认的输出格式是:日志级别:日志器名称:日志内容。而这样输出也是由于
logging模块提供的日志记录函数所使用的日志器设置的日志格式默认是BASIC_FORMAT,其值为:
"%(levelname)s:%(name)s:%(message)s"
详细的默认值能够看源码,当咱们没有提供任何配置信息的时候,这些函数都会去调用logging.basicConfig(**kwargs)
方法,且不会向该方法传递任何参数。继续查看basicConfig()
方法的代码就能够找到上面这些问题的答案了。
import logging LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p" logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT) logging.debug("This is a debug log.") logging.info("This is a info log.") logging.warning("This is a warning log.") logging.error("This is a error log.") logging.critical("This is a critical log.") # 此时会在my.log日志文件中看到下面的输出结果 # 08/12/2018 21:54:39 PM - DEBUG - This is a debug log. # 08/12/2018 21:54:39 PM - INFO - This is a info log. # 08/12/2018 21:54:39 PM - WARNING - This is a warning log. # 08/12/2018 21:54:39 PM - ERROR - This is a error log. # 08/12/2018 21:54:39 PM - CRITICAL - This is a critical log.
会上面这些基本的日志记录就算是掌握了。
说明:
logging.basicConfig()
函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起做用,后续再次调用该函数时彻底不会产生任何操做的,屡次调用的设置并非累加操做。RootLogger
类的实例,其名称为'root',它是处于日志器层级关系最顶层的日志器,且该实例是以单例模式存在的。logging.warning('%s is %d years old.', 'Tom', 10)
,输出内容为WARNING:root:Tom is 10 years old.
exc_info, stack_info, extra
,下面对这几个关键字参数做个说明。一个例子:
import logging LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s" DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p" logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT) logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'}) # 打印结果为 # 08/12/2018 22:01:43 PM - WARNING - Tom[192.168.199.22] - Some one delete the log file. # NoneType: None # Stack (most recent call last): # File "D:/workspace/modue/bin.py", line 6, in <module> # logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})
在说明用logging模块的四大组件记录日志以前, 颇有必要对logging模块所包含的重要组件以及其工做流程作个全面、简要的介绍,这有助于咱们更好的理解咱们所写的代码(将会触发什么样的操做)。
logging模块的四大组件
logging模块就是经过这些组件来完成日志处理的,上面所使用的logging模块级别的函数也是经过这些组件对应的类来实现的。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还能够经过过滤器(filter)和格式器(formatter)对要输出的日志内容作过滤和格式化等处理操做。
下面介绍下与logging四大组件相关的类:Logger,Handler,Filter,Formatter。
Logger对象有3个任务要作
logger对象最经常使用的方法分为两类:配置方法和消息发送。
建立一个Logger对象
logger = logging.Logger
logger = logging.getLogger()
当咱们建立了一个Logger对象,其实它并没什么功能,须要咱们给它配置添加。
经常使用的配置方法
(下面写的logger都是Logger类的实例化对象):
import logging logger = logging.getLogger() # 得到一个Logger实例化对象 fh = logging.FileHandler('my_log') # 建立一个FileHandler对象,用于写入日志文件,参数为日志写到哪一个文件中 ch = logging.StreamHandler() # 建立一个StreamHandler对象,用于输出到控制台 logger.addHandler(fh) # 为Logger对象添加FileHandler对象 logger.addHandler(ch) # 为Logger对象添加StreamHandler对象 logger.removeHandler(fh) # 为Logger对象删除FileHandler对象 logger.removeHandler(ch) # 为Logger对象删除StreamHandler对象
建立日志记录
例如:
logger = logging.getLogger() # 得到一个Logger实例化对象 logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')
说明:
关于logger的层级结构与有效等级的说明:
|
Handler对象的做用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。在上面的Logger类中,咱们使用了Handler类的一些知识。因此对Handler类应该有了必定的了解,这里会详细的介绍这个类。
Logger对象能够经过addHandler()方法为本身添加0个或者更多个handler对象。好比,一个应用程序可能想要实现如下几个日志需求:
这种场景就须要3个不一样的handlers,每一个handler复杂发送一个特定严重级别的日志到一个特定的位置。
经常使用的Handler
handle对象经常使用配置方法
Filter能够被Handler和Logger用来作比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只容许某个logger层级下的日志事件经过过滤。该类定义以下:
class logging.Filter(name='') filter(record)
好比,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只容许名称为相似以下规则的loggers产生的日志记录经过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。若是name的值为空字符串,则容许全部的日志事件经过过滤。
filter方法用于具体控制传递的record记录是否能经过过滤,若是该方法返回值为0表示不能经过过滤,返回值为非0表示能够经过过滤。
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不一样的是,应用代码能够直接实例化Formatter类。另外,若是你的应用程序须要一些特殊的处理行为,也能够实现一个Formatter的子类来完成。
Formatter类的构造方法定义以下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
可见,该构造方法接收3个可选参数:
下图大体表述了日志流的处理流程
咱们来描述下上面这个图的日志流处理流程:
可见,一条日志信息要想被最终输出须要依次通过如下几回过滤:
须要说明的是:
1.关于上面第9个步骤,若是propagate值为1,那么日志消息会直接传递交给上一级logger的handlers进行处理,此时上一级logger的日志等级并不会对该日志消息进行等级过滤。
2.使用getLogger()方法的时候,若是要返回的日志器名字相同,返回的为同一个日志器,由于它的内部是用单例生成的,因此当多个对象使用同一日志器而后进行设置时,新的设置会覆盖旧的设置。而当输出的时候,有几个对象要显示,就显示几遍日志。
3.日志器是一个树形结构,好比:getLogger("mylogger.sontree")mylogger是root的孩子,sontree是mylogger的孩子,root的孙子。
4.当咱们使用子层的日志器的时候,它会默认往上去找父级直到找到root,有几层显示几遍日志:
例如:
View Codeimport logging logger = logging.getLogger() # 得到一个Logger实例化对象,因为没写参数返回的root日志器 ch = logging.StreamHandler() # 建立一个StreamHandler对象,用于输出到控制台 logger.addHandler(ch) # 为Logger对象添加StreamHandler对象 logger.setLevel("ERROR") logger1 = logging.getLogger("my_log") # 得到一个名为Logger实例化对象 logger1.addHandler(ch) # # 为Logger对象添加StreamHandler对象 logger1.setLevel("DEBUG") logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message') logger1.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message') # 输出结果为: # logger error message # logger critical message # logger1 debug message # logger1 debug message # logger1 info message # logger1 info message # logger1 warning message # logger1 warning message # logger1 error message # logger1 error message # logger1 critical message # logger1 critical message例子中,logger不写参数获取的日志器为root,它是正常显示的,而logger1获取的则是名为my_log的日志器,它除了打印本身的,还向上去找,找到了它的父级就是顶级的root,因此也打印了一遍,这样就看到了后面打印了两遍的日志。你能够再按上面说到建立一个my_log日志器的子日志器进行测试。
5.上面提到了会重复打印的问题,那么我不想这样怎么办。那就是把例子中的logger.addHandler(ch) 注释了,不让父日志器的handler工做就好了。因此咱们须要注意的就是每当一个日志器的父日志器有输出的时候,它就会重复输出一次。
如今,咱们对logging模块的重要组件及整个日志流处理流程都应该有了一个比较全面的了解,下面咱们来看一个例子。
如今有如下几个日志记录的需求:
import logging import logging.handlers import datetime logger = logging.getLogger('mylogger') logger.setLevel(logging.DEBUG) rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0)) rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) f_handler = logging.FileHandler('error.log') f_handler.setLevel(logging.ERROR) f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s")) logger.addHandler(rf_handler) logger.addHandler(f_handler) logger.debug('debug message') logger.info('info message') logger.warning('warning message') logger.error('error message') logger.critical('critical message')
all.log文件输出
2018-08-13 11:00:58,246 - DEBUG - debug message
2018-08-13 11:00:58,247 - INFO - info message
2018-08-13 11:00:58,247 - WARNING - warning message
2018-08-13 11:00:58,247 - ERROR - error message
2018-08-13 11:00:58,247 - CRITICAL - critical message
error.log文件输出
2018-08-13 11:00:58,247 - ERROR - bin.py[:21] - error message
2018-08-13 11:00:58,247 - CRITICAL - bin.py[:22] - critical message
【转】 向日志输出中添加上下文信息:http://www.cnblogs.com/yyds/p/6897964.html
参考文档: