python的logging模块使用方法
logging模块简介
logging模块是Python内置的日志模块,用来生成程序的日志。一条日志对应一个事件的发生,一个事件一般包括:事件发生时间、事件发生位置、事件内容、事件严重程度-日志级别。(还可以包括进程ID、进程名称、线程ID、线程名称等)
logging模块的组成
- 日志级别:日志分为五个等级,从低到高分别是:DEBUG、INFO、WANGING、ERROR、CRITICAL
DEBUG:最详细的信息,通常定位问题的时候用
INFO:确认一切按照预期运行,详细程度仅次于DEBUG
WARNING:一些意想不到的问题发生了,或者不久的将来将要发生问题,比如磁盘空间小,软件还能正常运行。
ERROR:很严重的问题,软件没能正确执行某些功能。
CRITICAL:一个严重的错误,程序本身无法继续运行了。
当你的程序指定了一个日志级别后,程序会记录所有日志级别大于等于指定级别的log。比如指定的级别是WARNING,那么记录的将是WARNING、ERROR、CRITICAL。至于如何设置指定日志级别,将在后面说到。
- logging的4个组件:
logger(日志器):日志类,通过这个类提供的API来记录日志。
handler(处理器):对日志信息处理,可以将日志发送到不同的目标域中。
filter(过滤器):对日志信息进行过滤。
formatter(格式器):日志的格式化
logging模块的使用
3.1 logging模块定义的模块级别的函数
logging.debug(msg,*args,**kwargs):创建一条严重级别为debug的日志记录。
logging.info(msg,*args,**kwargs): 创建一条严重级别为info的日志记录。
logging.warning(msg,*args,**kwargs): 创建一条严重级别为warning的日志记录。
logging.error(msg,*args,**kwargs): 创建一条严重级别为error的日志记录。
logging.critical(msg,*args,**kwargs): 创建一条严重级别为critical的日志记录。
logging.log(level,*args,**kwargs): 创建一条严重级别为level的日志记录。
logging.basicConfig(**kwargs):对root logger进行一次性配置。用于指定要记录的日志级别,日志格式,日志输出位置,日志文件打开模式等信息。
举例:
logging.warning("This is a warning log")
输出:WARNING:root:This is a warning log
或者也可以用:logging.log(logging.WARNING,"This is a warning log")
输出:WARNING:root:This is a warning log
可以看出来上面两种方式,输出结果是一样的,结果有三部分组成,第一部分WARNING是日志级别,第二部分root是日志器名称,第三部发是具体的日志内容。这是因为logging模块有一个默认的输出格式“%(levelname)s:%(name)s:%(message)s”。没有手动设置格式的情况是就按照这种默认的日志格式输出了。日志器名称默认情况是是root,后面会讲到如何修改日志器名称。
另外要注意一点,如果你现在用logging.info("This is a info log"),或者logging. debug("This is a debug log"))你会发现控制台不会打印任何东西出来,为什么?也是因为logging模块的默认设置,默认为程序指定的日志级别是WARNING。所以只有大于等于这个级别的log会打印出来。
上面提到的所有默认设置都是在logging.basicConfig(**kwargs)中设置的,可以查看这个方法的源代码确认。
3.2 logging.basicConfig(**kwargs)方法
这个方法接收的参数是一个字典,字典的键为:
filename:指定日志输出目标文件的文件名,指定该项后日志信息就不会被输出到控制台了。
filemode:指定日志文件的打开模式,默认为”a”,需要注意的是,该选项在指定了filename后才有效。
format:指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。
datefmt:指定日期/时间格式,该选项要在format中包含时间字段%(asctime)时才有效。
level:指定日志器的日志级别。
stream:指定日志输出目标stream。如sys.stdout、sys.stderr以及网络stream。stream和filename不能同时提供。否则会引发ValueError异常。
style:指定format格式字符串的风格,默认为%,还可以为:’$’,’{’。
handlers:如果该选项存在,应该是一个创建了多个handlers的可迭代对象,这些handlers将会被添加到root logger中。该选项和stream以及filename只能存在一个。
举例:
# 设置日志级别,通过这个设置,就可以记录debug级别和info级别的log了
logging.basicConfig(level=logging.DEBUG)
logging.info("This is a info log")
输出:WARNING:root:This is a info log
3.3logging模块定义的格式字符串字段
下面是一些logging模块中定义好的可以用于format格式字符串中的字段:
字段/属性名称 |
使用格式 |
描述 |
asctime |
%(asctime)s |
日志事件发生的时间,比如:2019-12-23 11:04:10 ,050 |
created |
%(created)f |
日志事件发生的时间戳,即调用time.time()返回的值 |
relativeCreated |
%(relativeCreated)d |
日志事件发生的时间相对于logging模块加载时间的相对毫秒数 |
msecs |
%(msecs)d |
日志事件发生时间的毫秒部分 |
levelname |
%(levelname)s |
该日志记录的文字形式的日志级别,如DEBUG、INFO |
levelno |
%(levelno)s |
该日志记录的数值形式的日志级别(10,20,30,40,50) |
name |
%(name)s |
使用的日志器名称,默认为root |
message |
%(message)s |
日志记录的文本内容 |
pathname |
%( pathname)s |
调用日志记录函数的源码文件的全路径 |
filename |
%( filename)s |
pathname的文件名部分,包含文件后缀 |
module |
%(module)s |
filename的名称部分,不包含后缀 |
lineno |
%(lineno)d |
调用日志记录函数的源代码所在的行号 |
funcName |
%( funcName)s |
调用日志记录函数的函数名 |
process |
%( process)d |
进程ID |
processName |
%( processName)s |
进程名称 |
thread |
%( thread)d |
线程ID |
threadName |
%( threadName)s |
线程名称 |
举例:
import logging LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log")
在当前路径可以找到一个py.log文件,打开可以看到结果:
2019-12-23 11:23:04,213 - DEBUG - This is a debug log
2019-12-23 11:23:04,213 - WARNING - This is a warning log
日期时间看着有点别扭,可以修改为:
import logging LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%Y%m%d %H:%M:%S %p" logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log")
输出结果为:
20191223 11:28:16 AM - DEBUG - This is a debug log
20191223 11:28:16 AM - WARNING - This is a warning log
3.4补充说明
1.logging.basicConfig()是一个一次性简单配置工具,只有第一次调用的时候会起作用。
2.如果要记录的日志中包含变量数据,可以使用一个格式字符串作为这个事件的描述信息,然后将变量数据作为第二个参数*args的值进行传递,如logging.warning(‘%s is %d years old ’,’Tom’,10)
3.logging.debug()等方法中的**kwargs,支持3个关键字,exc_info,stack_info,extra.
exc_info:值为布尔值,如果该参数的值设置为true,则会将异常信息添加到日志信息中。如果没有就添加None到日志信息中。
stack_info:布尔值,默认值为False,如果设置为True,栈信息将会被添加到日志信息中。
extra:这是一个字典参数,可以用来定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突。
import logging LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s - %(user)s[%(ip)s]"
DATE_FORMAT = "%Y%m%d %H:%M:%S %p"
logging.basicConfig(filename="py.log", level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.debug("This is a debug log")
logging.warning("This is a warning log", extra={'user': 'Tom', 'ip': '192.168.0.0'})
输出的结果为:
20191223 14:11:18 PM - WARNING - This is a warning log - Tom[192.168.0.0]
logging模块的四大组件
四大组件上面说了有日志器、处理器、过滤器和格式器,日志器需要通过处理器将日志信息输出到目标位置,不同的处理器可以将日志输出到不同的位置。即日志器可以设置多个处理器将同一条日志输出到不同的位置,每一个处理器都可以设置自己的过滤器,实现日志过滤,从而只保留自己需要的日志;每一个处理器又可以分别设置自己的格式器,实现同一条日志以不同的格式输出到不同的位置。
4.1 Logger类
Logger对象常用的配置方法:
Logger.setLevel():设置日志器将会处理的最低级别日志
Logger.addHandler()和Logger.removeHandler():为Logger对象添加或移除一个Handler对象
Logger.addFilter()和Logger.removeFilter():为该logger对象添加或移除一个filter对象
通过上面的方法配置完成后,就可以创建日志记录了:
Logger.debug(),Logger.info(),Logger.warning(),Logger.error(),Logger.critical():创建一个debug、info、warning、error、critical等级的日志。
Logger.exception():创建一个类似于Logger.error()的日志消息。
Logger.log():需要获取一个明确的日志level参数来创建一个日志记录。
说明:
Logger.exception()与Logger.error()区别在于:Logger.exception()将会输出堆栈追踪信息,另外通常只是在一个exception handler中调用该方法。
Logger.log()与Logger.debug()等方法的区别:Logger.log()可以输出自定义level的日志。
怎样得到一个Logger对象?
一种方式是通过Logger类实例化得到一个事例,另外一种推荐使用的方法是通过调用logging.getLogger()方法,这个方法有一个可选参数叫name,表示将要返回的日志器名称,如果不传这个参数值,默认名称就是root,若以相同name参数多次调用getLogger()方法,返回的是同一个logger对象引用。
关于logger的层级结构与有效等级的说明
- logger的名称是一个以’.’分割的层级结构,每个’.’的后面是前面的children,例如foo.bar以及foo.bar.bam都是foo的后代。
- 后代如果没有设置最低level,则就去父代找level,父代也没有就去祖父代,都没有设置level的情况下默认就是warning
- child loggers完成对日志的处理后,就将日志消息传递给祖先loggers相关的handlers,所以一般给祖先配置一个handlers就足够了,也可以通过一个logger的propagate属性设置为False关闭传递。
4.2 Handlers类
Handler对象的作用是将不同level的日志分发到不同的handler指定的位置,比如需求为:
- 把所有日志都发送到一个日志文件中
- 把所有严重级别大于等于error的日志发送到stdout(标准输出)
- 把所有严重级别为critical的日志发送到一个Email邮件地址
这种情况下就需要三个不同的handlers,每一个handler负责发送一个level级别的日志到指定位置。
常用的方法:
Handler.setLevel() :设置handler将会处理的日志消息的最低严重级别
Handler.serFormatter():设置一个格式器对象
Handler.addFilter()和Handler.removeFilter():为handler添加和删除一个过滤器对象。
应用程序代码不应该直接实例化和使用Handler实例,因为Handler只是一个基类,提供子类重写覆盖的一些接口。
Handler |
描述 |
logging.StreamHandler |
将日志消息发送到输出到Stream,如std.out,std.err |
logging.FileHandler |
将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler |
将日志消息发送到磁盘文件,并支持日志文件按照大小切割 |
logging.handlers.TimeRotatingFileHandler |
将日志消息发送到磁盘文件,并支持日志文件按照时间切割 |
logging.handlers.HTTPHandler |
将日志消息以get或者post方式发送给HTTP服务器 |
longing.handlers.SMTPHandler |
将日志消息发送给指定的Email地址 |
logging.NullHandler |
该Handler实例会忽略error messages |
4.2 Formater类
Formater类用于定义日志输出的格式,内容
logging.Formatter.__init__(fmt=None, datefmt=None,style=’%’)
fmt:指定消息格式化字符串,如果不给值,就默认使用message原始内容
datefmt:指定日期格式字符串
style:指定format格式字符串的风格,默认为%,还可以为:’$’,’{’。
4.3Filter类
可以实现比level更复杂的过滤功能。Filter只允许某个logger层级下的日志事件通过过滤,
定义为:
class logging.Filter(name=’’):
filter(record)
比如:一个filter实例化时传递的name参数为’A.B’,那么该filter实例将允许名称为类似如下规则的loggers产生的日志记录通过过滤:‘A.B’,‘A.B.C’,‘A.B.D’,‘A.B.C.D’,而名称为‘A.BB’,‘B.A.B’的loggers产生的日志则会被过滤掉,如果name为空,则允许所有日志通过过滤。filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,非0表示可以通过过滤。
记录log的过滤流程:
1、 日志器等级过滤
2、 日志器的过滤器过滤
3、 日志器的处理器等级过滤
4、 日志器的处理器的过滤器过滤
4.4使用4大组件记录日志举例
需求:
记录所有的日志到all.log,格式为:日期和时间-日志级别-日志信息。
记录error等级以上的级别的log到error.log,格式为:日期和时间-日志级别-文件名[:行号]-日志信息。
all.log每天凌晨进行日志切割。
import logging.handlers
import datetime logger = logging.getLogger("mylogger")
logger.setLevel(logging.DEBUG) # 记录全部日志的句柄,all.log每天凌晨进行日志切割
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")) # 记录error级别以上的日志句柄
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')) # 将两个handler处理器分别给日志器,最后会生成两份日志
logger.addHandler(rf_handler)
logger.addHandler(f_handler) logger.debug("This is a debug log")
logger.error("This is a error log")
logger.info("This is a info log") # 子孙类日志事例
# 定义一个子孙类日志器,子孙类的日志会单独存一份log到error2,同时也会传递到父类的log中
logger2 = logging.getLogger('mylogger.son')
f_handler2 = logging.FileHandler('error2.log')
f_handler2.setLevel(logging.ERROR)
f_handler2.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s'))
logger2.addHandler(f_handler2)
logger2.debug("")
logger2.error("error333333") # 如果不想将子孙类的日志传递到父类中,使用属性propagate
logger2.propagate = False
logger2.info("")
logger2.error("error555")
另外还可以利用logging.config模块:
import logging.config # 通过字典配置日志模块
# 每次都要编写代码来配置非常麻烦,我们可以写一写完整的配置保存起来,以便后续直接使用 # 格式1 给开发看的
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s] [%(filename)s:%(lineno)d] [%(levelname)s] [%(message)s]'
logfile_path1 = "coder.log" # 格式2 给领导看的简略版
simple_format = '[%(levelname)s] [%(asctime)s] [%(message)s]'
logfile_path2 = 'boss.log' LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
# 打印到文件的日志,收集 DEBUG以上的日志
'std': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'standard',
'filename': logfile_path1,
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5, # 日志文件最大个数
'encoding': 'utf-8'
},
'boss': {
'level': INFO,
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'simple',
'filename': logfile_path2,
'maxBytes': 1024 * 1024 * 5, # 日志大小 5M
'backupCount': 5, # 日志文件最大个数
'encoding': 'utf-8'
}
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'aa': {
'handlers': ['std', 'console', 'boss'],
'level': DEBUG,
'propagate': 'True'
},
},
} logging.config.dictConfig(LOGGING_DIC)
logger = logging.getLogger("aa")
logger.error("testing error")
logger.debug("debug")
python的logging模块使用方法的更多相关文章
- Python之logging模块
一.引言 之前在写一些小程序的时候想把日志内容打到文件中,所以就自己写了一个logger.py的程序,如下: #!/usr/bin/python # -*- coding=utf-8 -*- impo ...
- python的logging模块
python提供了一个日志处理的模块,那就是logging 导入logging模块使用以下命令: import logging logging模块的用法: 1.简单的将日志打印到屏幕上 import ...
- python的logging模块之读取yaml配置文件。
python的logging模块是用来记录应用程序的日志的.关于logging模块的介绍,我这里不赘述,请参见其他资料.这里主要讲讲如何来读取yaml配置文件进行定制化的日志输出. python要读取 ...
- python中logging模块的用法
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...
- python基础--logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...
- Python中logging模块的基本用法
在 PyCon 2018 上,Mario Corchero 介绍了在开发过程中如何更方便轻松地记录日志的流程. 整个演讲的内容包括: 为什么日志记录非常重要 日志记录的流程是怎样的 怎样来进行日志记录 ...
- Python的logging模块详解
Python的logging模块详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.日志级别 日志级别指的是产生的日志的事件的严重程度. 设置一个级别后,严重程度 ...
- Python的logging模块基本用法
Python 的 logging 模块的简单用法 在服务器部署时,往往都是在后台运行.当程序发生特定的错误时,我希望能够在日志中查询.因此这里熟悉以下 logging 模块的用法. logging 模 ...
- python之 logging 模块(上篇)
一.日志关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变量数据的消息来描述.此外,事件 ...
随机推荐
- P1003 电影票价
题目描述 已知一位小朋友的电影票价是10元,请问 \(n\) 位小朋友的总票价是多少? 输入格式 输入一个整数 \(n(1 \le n \le 1000)\) , 用于表示小朋友的数量. 输出格式 输 ...
- vue权限控制菜单显示
对于不同角色显示不同的菜单 思路1: 本地放一份完整的菜单数据,通过后台返回角色的菜单列表两者对比,筛选需要显示的菜单数据绑定,这里有个问题就是路由vue实例初始化就生成了,加载的全部,人为输入地址是 ...
- SpringSecurity 自定义用户 角色 资源权限控制
SpringSecurity 自定义用户 角色 资源权限控制 package com.joyen.learning.security; import java.sql.ResultSet; impor ...
- Delta Lake源码分析
目录 Delta Lake源码分析 Delta Lake元数据 snapshot生成 日志提交 冲突检测(并发控制) delete update merge Delta Lake源码分析 Delta ...
- vc得到屏幕的当前分辨率方法
vc得到屏幕的当前分辨率方法:1.Windows API调用int width = GetSystemMetrics ( SM_CXSCREEN ); int height= GetSystemMet ...
- 2017.5.11 昨天晚上看fview直播坚果pro回放
对话1 彭林: 我以前也是产品经理,身为产品经理,你有没有感觉我们做出过什么之前不被人接受的东西,但是我们却坚持做了,并且得到的反响非常好. 朱萧木: 没有吧,我们没有做颠覆用户认知或者三观的特别 ...
- JAVA8学习——深入浅出函数式接口FunctionInterface(学习过程)
函数式接口 函数式接口详解:FunctionInterface接口 话不多说,先打开源码,查阅一番.寻得FunctionInterface接口 package java.util.function; ...
- MySQL的读写分离与主从同步数据一致性
有没有做MySQL读写分离?如何实现mysql的读写分离?MySQL主从复制原理的是啥?如何解决mysql主从同步的延时问题? 高并发这个阶段,那肯定是需要做读写分离的,啥意思?因为实际上大部分的互联 ...
- 【题解】P3645 [APIO2015]雅加达的摩天楼(分层图最短路)
[题解]P3645 [APIO2015]雅加达的摩天楼(分层图最短路) 感觉分层图是个很灵活的东西 直接连边的话,边数是\(O(n^2)\)的过不去 然而我们有一个优化的办法,可以建一个新图\(G=( ...
- 洛谷$P3324\ [SDOI2015]$星际战争 网络流+二分
正解:网络流+二分 解题报告: 传送门$QwQ$ 其实我第一反应是费用流来着,,,但是仔细想了下发现我不会实现各个武器之间独立同时?而且攻击是连续的答案可能是小数嘛$QwQ$. 所以显然不是递推就二分 ...