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



举例:

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---

渣图:

总结:

1. 每一个Logger实例的level如同入口,让水流进来,如果这个门槛太高,信息就进不来

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处理。

4. 父logger拿到消息,会重复第三条的过程,直至根logger

时间: 2024-11-05 22:35:36

[Python 多线程] logging模块、Logger类 (八)的相关文章

python之logging模块的使用

python的logging模块是用来写日志的,是python的标准模块. logging的结构 查看logging的python源码,可知主要有四个类实现功能: Loggers:提供应用程序直接使用的接口,如相关的配置设置: Handlers:将Loggers产生的日志传到指定位置,设置日志保存的位置: Filters:对输出日志进行过滤操作: Formatters:控制日志的输出格式: 日志记录的级别 DEBUG:优先级10,记录调试的详细信息,只在调试时开启: INFO:优先级20,记录普

Python中logging模块的基本用法

在 PyCon 2018 上,Mario Corchero 介绍了在开发过程中如何更方便轻松地记录日志的流程. 整个演讲的内容包括: 为什么日志记录非常重要 日志记录的流程是怎样的 怎样来进行日志记录 怎样进行日志记录相关配置 日志记录使用常见误区 下面我们来梳理一下整个演讲的过程,其实其核心就是介绍了 logging 模块的使用方法和一些配置. 日志记录的重要性 在开发过程中,如果程序运行出现了问题,我们是可以使用我们自己的 Debug 工具来检测到到底是哪一步出现了问题,如果出现了问题的话,

python多线程-threading模块

threading 是我们常用的用于 python 多线程的模块,其功能更加丰富.下面我们就来开始学习这个模块. 同样的,我这里声明一样我使用的版本是 python2.7,不同版本直接可能存在差异. 老规矩,使用 help() 函数获取帮助文档,看看里面有什么内容. threading 模块中提供了一个 thread 的类,注意不要和 thread 模块搞混了,两者差别还是很大的.thread 这个类可以实例化一个对象,每个对象代表一个线程,可以调用其中的 run() 方法来开启一个线程的运行.

Python基础-----logging模块

#!/usr/bin/env python#-*- coding:utf-8 -*- ########################################################################################################################################################灵活配置日志级别,日志格式,输出位置#####################################

python使用logging模块方法 教程

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息:print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据:logging则可以由开发者决定将信息输出到什么地方,以及怎么输出: logging模块的日志级别 logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日

python的logging模块

1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info message') logging.warning('This is warning message') 屏幕上打印: WARNING:root:Thisis warning message 默认情况下,logging将日志打印到屏幕,日志级别为WARNING: 日志级别大小关系为:CRITICAL > ER

Python 多线程threading模块

首先,我们在了解多线程时需要理解的就是什么是多线程,按照官方的解释就是:多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术. 在我自学到这里的时候,通过会在想进程和线程到底是有什么区别,我的理解就是: 进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,一个进程可以包含多个线程. 下面就以简单的例子来加强我们对python 线程的理解. 默认情况下,我们在没有启动线程的时候,可以看一下程序总的运行时间,应该是每个函数

python之logging模块使用

#!/usr/bin/env python # encoding: utf-8 import logging #定义handler的输出格式 formatter=logging.Formatter('%(asctime)s--%(name)s--%(filename)s--%(message)s') #创建一个handler,用于写入日志文件,只输出debug级别以上的日志 fh=logging.FileHandler('test.log') fh.setFormatter(formatter)

python中logging模块的一些简单用法

用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么了,但是当我需要看大量的地方或者在一个文件中查看的时候,这时候print就不大方便了,所以Python引入了logging模块来记录我想要的信息.print也可以输入日志,logging相对print来说更好控制输出在哪个地方,怎么输出及控制消息级别来过滤掉那些不需要的信息. 1.日志级别 import logging # 引入logging模块 # 将信息打印到控制台上 loggin