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 对应数字格式的日志级别 (DEBUGINFOWARNINGERROR,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 如果指定,这应该是一个已经创建的处理程序的迭代器添加到根记录器。任何尚未设置格式化程序的处理程序都将被分配在此函数中创建的默认格式化程序。
  1.  
 
 

举例:

  1. import threading
  2. import logging
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.INFO,format=FORMAT)
  5.  
  6. def add(x,y):
  7. logging.warning("{} {}".format(threading.enumerate(),x+y))
  8.  
  9. t = threading.Timer(1,add,args=(4,5))
  10. t.start()
  11.  
  12. 运行结果:
  13. 2017-12-17 15:40:34,226 123145367023616 [<_MainThread(MainThread, stopped 4320629568)>, <Timer(Thread-1, started 123145367023616)>] 9

  

修改日期格式:

  1. DATEFMT ="[%Y-%m-%d %H:%M:%S]"
  2. FORMAT = "%(asctime)s %(thread)d %(message)s"
  3. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)

  [2017-12-17 15:45:18]

输出到文件:

  1. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log')

  文件路径不指定,默认为当前模块路径。

  1. import threading
  2. import logging
  3. DATEFMT ="[%Y-%m-%d %H:%M:%S]"
  4. FORMAT = "%(asctime)s %(thread)d %(message)s"
  5. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log')
  6.  
  7. def add(x,y):
  8. logging.warning("{} {}".format(threading.enumerate(),x+y))
  9.  
  10. t = threading.Timer(1,add,args=(4,5))
  11. t.start()
  12.  
  13. 输出结果会追加写入当前模块路径的class_test.log文件:
  14. [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的后代。

举例:

  1. import logging
  2. DATEFMT ="[%Y-%m-%d %H:%M:%S]"
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT,filename='class_test.log')
  5.  
  6. root = logging.getLogger()
  7. print(root.name,type(root),root.parent,id(root))
  8.  
  9. logger = logging.getLogger(__name__)
  10. print(logger.name, type(logger), id(logger), id((logger.parent)))
  11.  
  12. logger1 = logging.getLogger(__name__ + ".ok")
  13. print(logger1.name, type(logger1), id(logger1), id((logger1.parent)))
  14.  
  15. print(logger1.parent,id(logger1.parent))
  16.  
  17. 运行结果:
  18. root <class 'logging.RootLogger'> None 4367575248
  19. __main__ <class 'logging.Logger'> 4367575864 4367575248
  20. __main__.ok <class 'logging.Logger'> 4367575920 4367575864
  21. <logging.Logger object at 0x10453eb38> 4367575864

  

子child的级别设置,不影响父parent的级别:

  1. import logging
  2.  
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
  5.  
  6. root = logging.getLogger()
  7. print(1,root,id(root)) #RootLogger,根Logger
  8. root.info('my root') #低于定义的WARNING级别,所以不会记录
  9.  
  10. loga = logging.getLogger(__name__) #Logger继承自RootLogger
  11. print(2,loga,id(loga),id(loga.parent))
  12. print(3,loga.getEffectiveLevel()) #数值形式的有效级别
  13.  
  14. loga.warning('before')
  15. loga.setLevel(28) #设置级别为28
  16. print(4,loga.getEffectiveLevel())
  17. loga.info('after')#
  18. loga.warning('after1')
  19.  
  20. 运行结果:
  21. [2017-12-17 16:31:20] 4320629568 before
  22. 1 <logging.RootLogger object at 0x104534f28> 4367535912
  23. 2 <logging.Logger object at 0x1044ef630> 4367250992 4367535912
  24. 3 30
  25. 4 28
  26. [2017-12-17 16:31:20] 4320629568 after1

  

Handler:

Handler控制日志信息的输出目的地,可以是控制台、文件。

可以单独设置level

可以单独设置格式

可以设置过滤器

Handler

  StreamHandler #不指定使用sys.strerr

    FileHandler #文件

    _StderrHandler #标准输出

  NullHandler #什么都不做

level的继承:

  1. import logging
  2.  
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
  5.  
  6. root = logging.getLogger() #根Logger级别为INFO 20
  7. print('root:',root.getEffectiveLevel())
  8.  
  9. log1 = logging.getLogger('s')
  10. log1.setLevel(logging.ERROR) #级别为ERROR 40
  11. print('log1:',log1.getEffectiveLevel())
  12. log1.error('log1 error')
  13.  
  14. log2 = logging.getLogger('s.s1') #继承自log1 40,无法使用warning
  15. log2.setLevel(logging.WARNING) #设置为WARNING 30,才可以使用warning
  16. print('log2:',log2.getEffectiveLevel())
  17. log2.warning('log2 warning')
  18.  
  19. 运行结果:
  20. [2017-12-17 16:52:22] 4320629568 log1 error
  21. root: 20
  22. log1: 40
  23. [2017-12-17 16:52:22] 4320629568 log2 warning
  24. log2: 30

  logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level。

handler处理:

  1. import logging
  2.  
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
  5.  
  6. root = logging.getLogger()
  7. print(1,root.getEffectiveLevel()) #RootLogger,根Logger
  8.  
  9. log1 = logging.getLogger('s')
  10. print(2,log1.getEffectiveLevel())
  11.  
  12. h1 = logging.FileHandler('test.log')
  13. h1.setLevel(logging.WARNING)
  14. log1.addHandler(h1)
  15. print(3,log1.getEffectiveLevel())
  16.  
  17. log2 = logging.getLogger('s.s2')
  18. print(4,log2.getEffectiveLevel())
  19.  
  20. h2 = logging.FileHandler('test1.log')
  21. h2.setLevel(logging.WARNING)
  22. log1.addHandler(h2)
  23. print(3,log1.getEffectiveLevel())
  24.  
  25. log2.warning('log2 info---')
  26.  
  27. 运行结果:
  28. 1 20
  29. [2017-12-17 19:02:53] 7956 log2 info---
  30. 2 20
  31. 3 20
  32. 4 20
  33. 3 20

  test.log和test1.log最终都会记录一份"log2 info---"

同样,handler也可以设置使用logging.Formatter()设置格式和Logging.Filter()设置过滤器:

  1. import logging
  2.  
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
  5.  
  6. root = logging.getLogger()
  7. print(1,root.getEffectiveLevel()) #RootLogger,根Logger
  8.  
  9. log1 = logging.getLogger('s')#模块化用__module__,函数化用__name__作为Logger名,Logger同名内存中也只有一个
  10. print(2,log1.getEffectiveLevel())
  11.  
  12. h1 = logging.FileHandler('test.log')
  13. h1.setLevel(logging.WARNING)
  14. fmt1 = logging.Formatter('[%(asctime)s] %(thread)s %(threadName)s log1-handler1 %(message)s')
  15. h1.setFormatter(fmt1) #重新个性化定义记录的格式化字符串
  16. log1.addHandler(h1)
  17. filter1 = logging.Filter('s') #过滤器 会记录s, s.s2的信息
  18. log1.addFilter(filter1)
  19. print(3,log1.getEffectiveLevel())
  20.  
  21. log2 = logging.getLogger('s.s2')
  22. print(4,log2.getEffectiveLevel())
  23.  
  24. h2 = logging.FileHandler('test1.log')
  25. h2.setLevel(logging.WARNING)
  26. log1.addHandler(h2)
  27. filter1 = logging.Filter('s.s2') #过滤器不会记录s.s2的消息,只会记录自己的消息
  28. log1.addFilter(filter1)
  29. print(3,log1.getEffectiveLevel())
  30.  
  31. log1.warning('log1 warning===')
  32. log2.warning('log2 warning---')
  33.  
  34. 运行结果:
  35. test.log: #handler1记录了到了log1和log2的信息
  36. [2017-12-17 19:43:12,654] 5872 MainThread log1-handler1 log1 warning===
  37. [2017-12-17 19:43:12,654] 5872 MainThread log1-handler1 log2 warning---
  38.  
  39. test1.log: #handler2只记录了它自己的信息
  40. log2 warning---

loggerLevel --> FilterConditions --> HandlerLevel --> 父LoggerFilter --> 父LoggerHandler --> RootHandler --> 标准输出或记录到日志:

  1. import logging,datetime
  2.  
  3. FORMAT = "%(asctime)s %(thread)d %(message)s"
  4. logging.basicConfig(level=logging.WARNING,format=FORMAT,datefmt="[%Y-%m-%d %H:%M:%S]")
  5.  
  6. "--------------root--------------------"
  7. root = logging.getLogger()
  8. print(1,root.getEffectiveLevel())
  9.  
  10. "--------------log1-------------------"
  11. log1 = logging.getLogger('s')
  12. log1.setLevel(logging.ERROR)
  13. print(2,log1.getEffectiveLevel())
  14.  
  15. h1 = logging.FileHandler('h1.log')
  16. h1.setLevel(logging.INFO)
  17. log1.addHandler(h1)
  18.  
  19. "--------------log2-------------------"
  20. log2 = logging.getLogger('s.s2')
  21. log2.setLevel(logging.WARNING)
  22. print(3,log2.getEffectiveLevel())
  23.  
  24. # h2 = logging.StreamHandler()
  25. h2 = logging.FileHandler('h2.log')
  26. h2.setLevel(logging.WARNING) # 最低40
  27. f2 = logging.Filter('s.s3') # log2 = s.s2 , s, s.s2 s.s3
  28. h2.addFilter(f2)
  29. log2.addHandler(h2)
  30.  
  31. log2.warning('4,log2 warning -- {}'.format(datetime.datetime.now()))
  32.  
  33. 运行结果:
  34. 1 30
  35. 2 40
  36. 3 30
  37. [2017-12-19 14:23:43] 2508 4,log2 warning -- 2017-12-19 14:23:43.364928
  38.  
  39. #h1.log
  40. 4,log2 warning -- 2017-12-19 14:23:43.364928
  41.  
  42. #h2.log
  43. 空,没有写入

  

官网流程图:

官网的介绍流程图中第三步,在检查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类的更多相关文章

  1. Python之logging模块

    一.引言 之前在写一些小程序的时候想把日志内容打到文件中,所以就自己写了一个logger.py的程序,如下: #!/usr/bin/python # -*- coding=utf-8 -*- impo ...

  2. python的logging模块之读取yaml配置文件。

    python的logging模块是用来记录应用程序的日志的.关于logging模块的介绍,我这里不赘述,请参见其他资料.这里主要讲讲如何来读取yaml配置文件进行定制化的日志输出. python要读取 ...

  3. Python全栈之路----常用模块----logging模块

    很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...

  4. python中logging模块的用法

    很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...

  5. Python模块——logging模块

    logging模块简介 logging模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统.logging模块是Python的一个标准库模块, 由标准库模块提供日志记录API的关键好处是 ...

  6. python基础--logging模块

    很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,loggin ...

  7. Python中logging模块的基本用法

    在 PyCon 2018 上,Mario Corchero 介绍了在开发过程中如何更方便轻松地记录日志的流程. 整个演讲的内容包括: 为什么日志记录非常重要 日志记录的流程是怎样的 怎样来进行日志记录 ...

  8. Python日志(logging)模块,shelve,sys模块

    菜鸟学python第十七天 1.logging 模块 logging模块即日志记录模块 用途:用来记录日志 为什么要记录日志: 为了日后复查,提取有用信息 如何记录文件 直接打开文件,往里写东西 直接 ...

  9. Python的logging模块详解

          Python的logging模块详解 作者:尹正杰  版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.日志级别 日志级别指的是产生的日志的事件的严重程度. 设置一个级别后,严重程度 ...

  10. python之 logging 模块(上篇)

    一.日志关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变量数据的消息来描述.此外,事件 ...

随机推荐

  1. 配置内核源码make menuconfig时出现 #include CURSES_LOC错误

    配置内核时出现如下错误: liuxin@sunshine-virtual-machine:~/work/nfs_root/system/linux-2.6.22.6$ make menuconfig ...

  2. Java基础教程(1)--概述

    一.什么是Java语言   Java是于1996年由Sun公司发布的一种极富创造力的面向对象的程序设计语言.它不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java ...

  3. 第一个Windows窗口应用程序

    学习目的 熟悉开发工具Visual C++ 6.0和MSDN 2001的使用. 应用Windows API函数, 手工编写具有最基本构成的Windows窗口应用程序(包含WinMain入口函数, 消息 ...

  4. DOM基础操作(一)

    DOM的基本操作有四种,我们会逐一给大家进行展示 增加操作 1.创建元素节点 createElement 我们可以通过document.createElement(‘div’);这个方法来创建一个元素 ...

  5. MySql:局域网和权限用户管理

    MySql 5.6(XP)/5.7(win7) 添加用户和设置局域访问权限操作.请在 http://sourceforge.net/  下载MySql Control Center(不是安装版本). ...

  6. Web前端面试指导(十八):用纯CSS创建一个三角形的原理是什么?

    题目点评 三角形的图标在网页设计是很常见的,属于基本常识题,只要在练习做到过这个功能都能回答出来,可以把你做过的思路描述出来就可以了,本题的难易程度为简单 答题要点 1.采用的是均分原理 盒子都是一个 ...

  7. netstat统计

    状态统计 netstat -ant | awk '/tcp/ {print $6}'|sort |uniq -c |sort -nr 前十位ESTABLISHED状态ip统计 netstat -ant ...

  8. Android浮动按钮

    https://www.jianshu.com/p/18cbc862ba7b https://github.com/yhaolpz/FloatWindow 这样就解决了切换 Activity 时悬浮控 ...

  9. 没有对比就没有伤害,memcache and redis

    Memcached 与 Redis 的关键性能指标比较 性能对比: Redis 只使用单核,而 Memcached 可以使用多核,所以平均每一个核上 Redis在存储小数据时比 Memcached 性 ...

  10. ajax实现跨域请求

    因为现在一直用的mvc,所以就以mvc来说说ajax跨域提交. 首先说说跨域,简单说就是不同域名访问,比如在aaa.com访问bbb.com. 就拿招聘网站来说,分为两种用户,求职者和企业,求职者端是 ...