[Python 模块] logging模块、Logger类
logging模块:
标准库里面的logging模块,在前面学习线程安全时曾用来解决print被打断的问题,这里会介绍logging模块的功能。
logging模块是线程安全的,不需要客户做任何特殊的工作。它通过使用线程锁实现了这一点; 有一个锁来序列化访问模块的共享数据,每个处理程序还创建一个锁来序列化访问其底层 I/O。
日志记录级别:
级别 | 数值 |
---|---|
CRITICAL |
50 |
ERROR |
40 |
WARNING |
30,默认 |
INFO |
20 |
DEBUG |
10 |
NOTSET |
0 |
定义的记录级别越低,信息越多,级别越高,信息越少。
日志记录格式化字符串:
属性名 | 格式 | 描述 |
---|---|---|
asctime | %(asctime)s |
易读的时间格式: 默认情况下是'2003-07-08 16:49:45,896'的形式(逗号之后的数字是毫秒部分的时间) |
filename | %(filename)s |
路径名的文件名部分。 |
funcName | %(funcName)s |
日志调用所在的函数名 |
levelname | %(levelname)s |
消息的级别名称('DEBUG' , 'INFO' , 'WARNING' , 'ERROR' , 'CRITICAL' ). |
levelno | %(levelno)s |
对应数字格式的日志级别 (DEBUG , INFO , WARNING , ERROR ,CRITICAL ). |
lineno | %(lineno)d |
发出日志记录调用的源码行号 (如果可用)。 |
module | %(module)s |
所在的模块名(如test6.py模块则记录test6) |
message | %(message)s |
记录的信息 |
name | %(name)s |
调用的logger记录器的名称 |
process | %(process)d |
进程ID |
processName | %(processName)s |
进程名 |
thread | %(thread)d |
线程ID |
threadName | %(threadName)s |
线程名 |
使用basicConfig方法配置logging记录格式:
格式 | 描述 |
---|---|
filename |
指定使用指定的文件名而不是StreamHandler创建FileHandler。 |
filemode |
指定打开文件的模式,如果指定了filename(如果文件模式未指定,则默认为'a')。 |
format |
为处理程序使用指定的格式字符串。 |
datefmt |
使用指定的日期/时间格式。 |
level |
将根记录器级别设置为指定的级别。 |
handlers |
如果指定,这应该是一个已经创建的处理程序的迭代器添加到根记录器。任何尚未设置格式化程序的处理程序都将被分配在此函数中创建的默认格式化程序。 |
举例:
import threading
import logging
FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT) def add(x,y):
logging.warning("{} {}".format(threading.enumerate(),x+y)) t = threading.Timer(1,add,args=(4,5))
t.start() 运行结果:
2017-12-17 15:40:34,226 123145367023616 [<_MainThread(MainThread, stopped 4320629568)>, <Timer(Thread-1, started 123145367023616)>] 9
修改日期格式:
DATEFMT ="[%Y-%m-%d %H:%M:%S]"
FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)
[2017-12-17 15:45:18]
输出到文件:
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log')
文件路径不指定,默认为当前模块路径。
import threading
import logging
DATEFMT ="[%Y-%m-%d %H:%M:%S]"
FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log') def add(x,y):
logging.warning("{} {}".format(threading.enumerate(),x+y)) t = threading.Timer(1,add,args=(4,5))
t.start() 输出结果会追加写入当前模块路径的class_test.log文件:
[2017-12-17 15:50:13] 123145503244288 [<_MainThread(MainThread, stopped 4320629568)>, <Timer(Thread-1, started 123145503244288)>] 9
Logger类:
构造
使用工厂方法返回一个Logger实例。
logging.
getLogger
([name=None])
指定name,返回一个名称为name的Logger实例。如果再次使用相同的名字,是实例化一个对象。未指定name,返回Logger实例,名称是root,即根Logger。
Logger是层次结构的,使用 '.' 点号分割,如'a'、'a.b'或'a.b.c.d','a'是'a.b'的父parent,a.b是a的子child。对于foo来说,名字为foo.bar、foo.bar.baz、foo.bam都是foo的后代。
举例:
import logging
DATEFMT ="[%Y-%m-%d %H:%M:%S]"
FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log') root = logging.getLogger()
print(root.name,type(root),root.parent,id(root)) logger = logging.getLogger(__name__)
print(logger.name, type(logger), id(logger), id((logger.parent))) logger1 = logging.getLogger(__name__ + ".ok")
print(logger1.name, type(logger1), id(logger1), id((logger1.parent))) print(logger1.parent,id(logger1.parent)) 运行结果:
root <class 'logging.RootLogger'> None 4367575248
__main__ <class 'logging.Logger'> 4367575864 4367575248
__main__.ok <class 'logging.Logger'> 4367575920 4367575864
<logging.Logger object at 0x10453eb38> 4367575864
子child的级别设置,不影响父parent的级别:
import logging FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]") root = logging.getLogger()
print(1,root,id(root)) #RootLogger,根Logger
root.info('my root') #低于定义的WARNING级别,所以不会记录 loga = logging.getLogger(__name__) #Logger继承自RootLogger
print(2,loga,id(loga),id(loga.parent))
print(3,loga.getEffectiveLevel()) #数值形式的有效级别 loga.warning('before')
loga.setLevel(28) #设置级别为28
print(4,loga.getEffectiveLevel())
loga.info('after')#
loga.warning('after1') 运行结果:
[2017-12-17 16:31:20] 4320629568 before
1 <logging.RootLogger object at 0x104534f28> 4367535912
2 <logging.Logger object at 0x1044ef630> 4367250992 4367535912
3 30
4 28
[2017-12-17 16:31:20] 4320629568 after1
Handler:
Handler控制日志信息的输出目的地,可以是控制台、文件。
可以单独设置level
可以单独设置格式
可以设置过滤器
Handler
StreamHandler #不指定使用sys.strerr
FileHandler #文件
_StderrHandler #标准输出
NullHandler #什么都不做
level的继承:
import logging FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]") root = logging.getLogger() #根Logger级别为INFO 20
print('root:',root.getEffectiveLevel()) log1 = logging.getLogger('s')
log1.setLevel(logging.ERROR) #级别为ERROR 40
print('log1:',log1.getEffectiveLevel())
log1.error('log1 error') log2 = logging.getLogger('s.s1') #继承自log1 40,无法使用warning
log2.setLevel(logging.WARNING) #设置为WARNING 30,才可以使用warning
print('log2:',log2.getEffectiveLevel())
log2.warning('log2 warning') 运行结果:
[2017-12-17 16:52:22] 4320629568 log1 error
root: 20
log1: 40
[2017-12-17 16:52:22] 4320629568 log2 warning
log2: 30
logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level。
handler处理:
import logging FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]") root = logging.getLogger()
print(1,root.getEffectiveLevel()) #RootLogger,根Logger log1 = logging.getLogger('s')
print(2,log1.getEffectiveLevel()) h1 = logging.FileHandler('test.log')
h1.setLevel(logging.WARNING)
log1.addHandler(h1)
print(3,log1.getEffectiveLevel()) log2 = logging.getLogger('s.s2')
print(4,log2.getEffectiveLevel()) h2 = logging.FileHandler('test1.log')
h2.setLevel(logging.WARNING)
log1.addHandler(h2)
print(3,log1.getEffectiveLevel()) log2.warning('log2 info---') 运行结果:
1 20
[2017-12-17 19:02:53] 7956 log2 info---
2 20
3 20
4 20
3 20
test.log和test1.log最终都会记录一份"log2 info---"
同样,handler也可以设置使用logging.Formatter()设置格式和Logging.Filter()设置过滤器:
import logging FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]") root = logging.getLogger()
print(1,root.getEffectiveLevel()) #RootLogger,根Logger log1 = logging.getLogger('s')#模块化用__module__,函数化用__name__作为Logger名,Logger同名内存中也只有一个
print(2,log1.getEffectiveLevel()) h1 = logging.FileHandler('test.log')
h1.setLevel(logging.WARNING)
fmt1 = logging.Formatter('[%(asctime)s] %(thread)s %(threadName)s log1-handler1 %(message)s')
h1.setFormatter(fmt1) #重新个性化定义记录的格式化字符串
log1.addHandler(h1)
filter1 = logging.Filter('s') #过滤器 会记录s, s.s2的信息
log1.addFilter(filter1)
print(3,log1.getEffectiveLevel()) log2 = logging.getLogger('s.s2')
print(4,log2.getEffectiveLevel()) h2 = logging.FileHandler('test1.log')
h2.setLevel(logging.WARNING)
log1.addHandler(h2)
filter1 = logging.Filter('s.s2') #过滤器不会记录s.s2的消息,只会记录自己的消息
log1.addFilter(filter1)
print(3,log1.getEffectiveLevel()) log1.warning('log1 warning===')
log2.warning('log2 warning---') 运行结果:
test.log: #handler1记录了到了log1和log2的信息
[2017-12-17 19:43:12,654] 5872 MainThread log1-handler1 log1 warning===
[2017-12-17 19:43:12,654] 5872 MainThread log1-handler1 log2 warning--- test1.log: #handler2只记录了它自己的信息
log2 warning---
loggerLevel --> FilterConditions --> HandlerLevel --> 父LoggerFilter --> 父LoggerHandler --> RootHandler --> 标准输出或记录到日志:
import logging,datetime FORMAT = "%(asctime)s %(thread)d %(message)s"
logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]") "--------------root--------------------"
root = logging.getLogger()
print(1,root.getEffectiveLevel()) "--------------log1-------------------"
log1 = logging.getLogger('s')
log1.setLevel(logging.ERROR)
print(2,log1.getEffectiveLevel()) h1 = logging.FileHandler('h1.log')
h1.setLevel(logging.INFO)
log1.addHandler(h1) "--------------log2-------------------"
log2 = logging.getLogger('s.s2')
log2.setLevel(logging.WARNING)
print(3,log2.getEffectiveLevel()) # h2 = logging.StreamHandler()
h2 = logging.FileHandler('h2.log')
h2.setLevel(logging.WARNING) # 最低40
f2 = logging.Filter('s.s3') # log2 = s.s2 , s, s.s2 s.s3
h2.addFilter(f2)
log2.addHandler(h2) log2.warning('4,log2 warning -- {}'.format(datetime.datetime.now())) 运行结果:
1 30
2 40
3 30
[2017-12-19 14:23:43] 2508 4,log2 warning -- 2017-12-19 14:23:43.364928 #h1.log
4,log2 warning -- 2017-12-19 14:23:43.364928 #h2.log
空,没有写入
官网流程图:
官网的介绍流程图中第三步,在检查Filter过滤条件不满足时就直接终止。但在上面例子中,log2 --> h2 --> h2Filter --> h2Handler ,当log2传入的消息级别不满足h2Filter条件时,没有直接终止,而是继续向上传递给了父logger的Handler(h1),满足h1级别,最后写入了h1.log。所以,官网上这一步不知道是不是正确的,但上面例子的实验结果却又证明官网的流程是错误的。
总结:
1. 每一个Logger实例的level如同入口,让水流进来,如果这个门槛太高,信息就进不来。例如log2.info('log3 info'),如果log2定义的级别高于info级别,就不会又信息通过log2
2. 如果level没有设置,就用父logger的,如果父logger的level也没有设置,继续找父的父的,最终找到root上,如果root设置了就用它的,如果root没有设置,root的默认值是WARNING
3.消息传递流程:
在某个logger上产生某种级别的信息,首先和logger的level检查,如果消息level低于logger的EffectiveLevl有效级别,消息丢弃,不会再向父logger传递该消息。如果通过(大于等于)检查后,则把消息交给logger所有的handler处理,每一个handler需要和自己level比较来决定是否处理。
如果没有一个handler,或者消息已经被handler处理过了,则需要通过本logger的propagate属性是否为True,Ture则把这个消息会继续发给父Logger,当前Logger的父Logger称为当前Logger,新的Logger的所有Handler继续处理消息。
4. logger实例初始的propagate属性为True,即允许想父logger传递消息
5. logger.basicConfig
如果root没有handler,就默认创建一个StreamHandler,如果设置了filename,就创建一个FileHandler。如果设置了format参数,就会用它生成一个formatter对象,并把这个formatter加入到刚才创建的handler上,然后把这些handler加入到root.handlers列表上。level 是设置给root.logger的。
如果root.handlers列表不为空,logging.basicConfig的调用什么都不做。
[Python 模块] logging模块、Logger类的更多相关文章
- Python之logging模块
一.引言 之前在写一些小程序的时候想把日志内容打到文件中,所以就自己写了一个logger.py的程序,如下: #!/usr/bin/python # -*- coding=utf-8 -*- impo ...
- python的logging模块之读取yaml配置文件。
python的logging模块是用来记录应用程序的日志的.关于logging模块的介绍,我这里不赘述,请参见其他资料.这里主要讲讲如何来读取yaml配置文件进行定制化的日志输出. python要读取 ...
- Python全栈之路----常用模块----logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...
- python中logging模块的用法
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...
- Python模块——logging模块
logging模块简介 logging模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统.logging模块是Python的一个标准库模块, 由标准库模块提供日志记录API的关键好处是 ...
- python基础--logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...
- Python中logging模块的基本用法
在 PyCon 2018 上,Mario Corchero 介绍了在开发过程中如何更方便轻松地记录日志的流程. 整个演讲的内容包括: 为什么日志记录非常重要 日志记录的流程是怎样的 怎样来进行日志记录 ...
- Python日志(logging)模块,shelve,sys模块
菜鸟学python第十七天 1.logging 模块 logging模块即日志记录模块 用途:用来记录日志 为什么要记录日志: 为了日后复查,提取有用信息 如何记录文件 直接打开文件,往里写东西 直接 ...
- Python的logging模块详解
Python的logging模块详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.日志级别 日志级别指的是产生的日志的事件的严重程度. 设置一个级别后,严重程度 ...
- python之 logging 模块(上篇)
一.日志关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变量数据的消息来描述.此外,事件 ...
随机推荐
- Vue2.0学习笔记:Vue事件修饰符的使用
事件处理 如果需要在内联语句处理器中访问原生DOM事件.可以使用特殊变量$event,把它传入到methods中的方法中. 在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间 ...
- CF898A Rounding
题意翻译 给你一个数字,将其“四舍六入”,末尾为5舍去或进位都可,求最终的数字. 题目描述 Vasya has a non-negative integer n n n . He wants to r ...
- EF fluent API如何配置主键不自动增长
在Dbcontext中作如下添加: protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilde ...
- 局域网内配置虚拟机的hostname
一般上我们在局域网内访问,比如宿主机访问虚拟机的时候可以直接使用IP去访问,大多数情况下也都适用.但是对于有的情况,比如像新版的hbase的配置,它默认将localhost作为hbase.master ...
- 解决:IDEA springmvc maven 项目搭建完后没有生成 webcontent 目录
前言:发现项目创建好,配置好,写好测试代码,一看没有 webcontent 目录. 问题: 解决方案: ctrl + alt + Shift + S
- C# Task.FromResult的用法
Task.FromResult用来创建一个带返回值的.已完成的Task. 场景一:以同步的方式实现一个异步接口方法比如有一个接口包含异步方法. interface IMyInterface { Tas ...
- LeetCode赛题515----Find Largest Element in Each Row
问题描述 You need to find the largest element in each row of a Binary Tree. Example: Input: 1 / \ 2 3 / ...
- 安卓app开发-01-开发工具及环境配置
安卓app开发-01-开发工具及环境配置 请大家根据推荐指数和自己的意愿选择 使用 Android Studio 1.可以使用 Android Studio 下载地址:http://www.andro ...
- Python爬虫教程-04-response简介
Spider-04-response简介 本小节介绍urlopen的返回对象,和简单调试方法 案例v3 研究request的返回值,输出返回值类型,打印内容 geturl:返回请求对象的url inf ...
- HTML5-入门2。
文本样式 <b>加粗</b> <i>倾斜</i> <u>下划线</u> <s>删除线</s> < ...