Python标准库之Logging库

logging——Python的日志记录工具

当前版本:3.12.6

记录日期:2024.10.08

官方文档:https://docs.python.org/zh-cn/3/library/logging.html

这个模块为应用与库实现了灵活的事件日志系统的函数与类。

共包含4个模块:

  1. 记录器:logging.Logger
  2. 处理器:logging.Handler
  3. 格式器:logging.Formatter
  4. 过滤器:logging.Filter

另外还有3类辅助功能:logging模块级函数、LogRecord属性、LoggerAdapter对象

最常用的是先用模块级函数logging.getLogger(name)函数创建日志记录器实例,然后使用模块级函数logging.basicConfig()设置基本配置(也可以使用处理器配合格式器、过滤器等进行基础配置),通过自定义logging库的Handler、Formatter、LogRecord、LoggerAdapter等模块可以实现高度定制自己的日志输出。

使用标准库提供的logging API最主要的好处是,所有的Python模块都可能参与日志输出,包括你自己的日志消息和第三方模块的日志消息。

模块级别日志记录器-——示例:

# myapp.py

import logging

import mylib

logger = logging.getLogger(__name__)

def main():

    logging.basicConfig(filename='myapp.log', level=logging.INFO)

    logger.info('Started')

    mylib.do_something()

    logger.info('Finished')

if __name__ == '__main__':

main()

# mylib.py

import logging

logger = logging.getLogger(__name__)

def do_something():

logger.info('Doing something')

如果你运行 myapp.py ,你应该在 myapp.log 中看到:

INFO:__main__:Started

INFO:mylib:Doing something

INFO:__main__:Finished

这种惯常用法的一个关键特性在于大部分代码都是简单地通过getLogger(__name__)创建一个模块级别的日志记录器,并使用该日志记录器来完成任何需要的日志记录。这样既简洁明了,又能根据需要对下游代码进行细粒度的控制。记录到模块级日志记录器的消息会被转发给更高级别模块的日志记录器的处理器,一直到最高层级的日志记录器既日志记录器;这种方式被称为分级日志记录。

要使日志记录有用,就需要对其进行配置:为每个日志记录器设置级别和目标,还可能改变特定模块的日志记录方式,通常是基于命令行参数或应用配置来实现。在大多数情况下,如上文所述,只有根日志记录器需要如此配置,因为所有在模块层级上的低级别日志记录器最终都会将消息转发给它的处理器。basicConfig()提供了一种配置根日志记录器的快捷方式,它可以处理多种应用场景。

该模块定义的基础类,以及它们的属性和方法都在下面的小节中列出。

  1. 记录器暴露了应用程序代码直接使用的接口
  2. 处理器将日志记录(由记录器创建)发送到适当的目标。
  3. 过滤器提供了更细粒度的功能,用于确定要输出的日志记录。
  4. 格式器指定最终输出中日志记录的样式。

目录

logging——Python的日志记录工具 1

一、记录器对象 9

class logging.Logger 9

【属性6个】 9

1、name 9

——示例: 9

2、level 9

——示例: 10

3、parent 10

——示例: 10

4、propagate 10

——示例: 11

5、handlers 11

——示例: 11

6、disabled 11

——示例: 11

【方法20个】 12

1、debug(msg, *args, **kwargs) 12

2、info(msg, *args, **kwargs) 13

3、warning(msg, *args, **kwargs) 13

4、error(msg, *args, **kwargs) 13

5、critical(msg, *args, **kwargs) 13

6、log(level, msg, *args, **kwargs) 13

7、setLever(level) 13

——示例: 14

8、isEnabledFor(level) 14

——示例: 14

9、getEffectiveLevel() 14

——示例: 14

10、getChild(suffix) 15

11、getChildren() 15

12、exception(msg, *args, **kwargs) 15

13、addFilter(filter) 15

14、removeFilter(filter) 15

15、addHandler(hdlr) 15

16、removeHanler(hdlr) 16

17、findCaller(stack_info=False,stacklever=1) 16

18、handle(record) 16

19、makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None) 16

20、hasHandlers() 16

——示例:展示如何使用Logger类的属性及方法 16

日志级别(6个) 18

1、logging.NOTSET 18

2、logging.DEBUG 18

3、logging.INFO 18

4、loging.WARNING 18

5、logging.ERROR 18

6、logging.CRITICAL 18

二、处理器对象 19

class logging.Handler 19

【方法15个】 19

1、__init__(level=NOTSET) 19

2、createLock() 19

3、acquire() 19

4、release() 19

5、setLevel(level) 19

6、setFormatter(fmt) 20

7、addFilter(filter) 20

8、removeFilter(filter) 20

9、filter(record) 20

10、flush() 20

11、close() 20

12、handle(record) 20

13、handleError(record) 20

14、format(record) 21

15、emit(record) 21

——示例1:如何创建自定义的handler类,并使用它来处理日志记录 21

三、格式器对象 23

class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None) 23

【参数(5个)】 23

1、fmt(str) 23

2、datefmt(str) 23

3、style(str) 23

4、validate(bool) 23

5、defaults(dict[str, Any]) 24

【方法4个】 24

1、format(record) 24

——示例: 24

2、formatTime(record, datefmt=None) 25

3、fromatException(exc_info) 25

参数 25

——示例:如何使用logging模块来记录异常信息 26

4、formatStack(stack_info) 26

class logging.BufferingFormatter(linefmt=None) 26

【方法3个】 26

1、formatHeader(records) 26

2、formatFooter(records) 27

3、format(records) 27

四、过滤器对象 28

——作用 28

控制日志输出 28

实现复杂的过滤逻辑 28

提高性能 28

——示例1: 只记录包含"重要"字符串的记录 28

class logging.Filter(name='') 29

filter(record) 29

五、LogRecord属性 30

class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None) 30

【参数9个】 30

1、name(str) 30

2、level(int) 31

3、pathname(str) 31

4、lineno(int) 31

5、msg(Any) 31

6、args(tuple|dict[str,Any]) 31

7、exc_info(tuple[type[BaseException],BaseException,types.TracebackType]|None) 31

8、func(str|None) 31

9、sinfo(str|None) 31

——示例1:展示如何使用LogRecord类来创建自定义日志记录 32

LogRecord属性 33

六、LoggerAdapter对象 34

class logging.LoggerAdapter(logger,extra) 34

——示例1 35

【方法】 36

process(msg,kwargs) 36

——示例1 36

manager 37

_log方法 37

七、线程安全 37

八、模块级函数(20个) 37

1、logging.getLogger(name=None) 37

——参数 38

name(可选): 38

返回值 38

——示例1:获取默认Logger 38

——示例2:获取命名Logger 38

——示例3:获取层次结构Logger 39

——示例4 39

2、logging.basicConfig(**kwargs) 40

——可用参数 40

filename 40

filemode 40

format 41

datefmt 41

——示例: 41

style 42

例: 42

level 43

stream 43

handlers 43

——示例: 44

force 44

——示例: 45

encoding 45

——示例: 45

errors 46

——示例: 46

版本变化 46

——示例: 47

3、logging.debug(msg,*args,**kwargs) 47

——示例1 48

4、logging.info(msg,*args,**kwargs) 48

5、logging.warning(msg,*args,**kwargs) 48

6、logging.error(msg,*args,**kwargs) 48

7、logging.critical(msg,*args,**kwargs) 48

8、logging.exception(msg,*args,**kwargs) 48

9、logging.log(level,msg,*args,**kwargs) 48

10、logging.disable(level=CRITICAL) 49

11、logging.getLoggerClass() 49

作用 49

——示例1:获取当前日志记录器的类 49

——示例2:自定义一个日志记录器类,并添加自定义方法,使用setLoggerClass()方法将其设置为新的日志记录器类 49

12、logging.getLogRecordFactory() 50

返回值 50

作用 50

——示例1:获取当前的日志记录工厂函数 50

——示例2:自定义日志记录工厂函数 51

13、logging.addLevelName(level,levelName) 51

作用 51

参数 51

——示例1:添加自定义日志级别 52

——示例2:为现有日志级别添加名称 52

——示例3:查看所有日志级别名称 53

14、logging.getLevelNamesMapping() 53

作用 53

返回值 53

——示例1:获取当前日志级别与数值映射 53

15、logging.getLevelName(level) 54

——示例1:获取标准日志级别名称 54

——示例2:通过日志级别数值获取对应级别名称 54

16、logging.getHandlerByName(name) 54

17、logging.makeLogRecord(attrdict) 54

——示例1:创建基本的LogRecord对象 55

18、logging.shutdown() 56

19、logging.setLoggerClass(class) 56

——示例1:自定义日志记录器类 56

——示例2:使用自定义记录器 57

——示例3:恢复默认日志记录器类 57

20、logging.setLogRecordFactory(factory) 57

作用 57

参数 57

——示例:定义自定义日志记录工厂 59

九、模块级属性(2个) 60

1、logging.lastResort 60

作用 60

属性说明 60

——示例1:查看lastResort的处理器 60

——示例2:#触发 lastResort: 如果没有配置任何处理器,记录日志时将使用 lastResort。 60

——示例3:#自定义 lastResort: 你可以自定义 lastResort 的处理器,例如更改其输出流或级别。 61

2、logging.raiseExceptions 61

作用 61

默认值 61

——示例1:查看当前设置 62

十、与警告模块集成 63

logging.captureWarnings(capture) 63

作用 63

默认值 63

——示例1:捕获warings到日志中 63

十一、笔记问答 65

问:Python中有哪些常见编码可以设置 65

GBK 65

GB18030 65

GB2312(淘汰) 65

UTF-8(最常用) 65

ASCII 66

Big5 66

ISO-8859-1 66

Windows-1252 66

Shift_JIS 66

问:logging模块中,格式化字符串的三种不同格式的区别及用法 67

一、%风格 67

二、{}格式 67

三、$风格 67

一、记录器对象

class logging.Logger

(class logging.Logger),记录器有以下的属性和方法。

注意永远不要直接实例化记录器,应当通过模块级别的函数logging.getLogger(name)。多次使用相同的名字调用getLogger()会一直返回相同的Logger对象的引用。

name一般是以英文句点分割的层级值,如foo.bar.baz(尽管也可以是简单形式,例如foo)。层级结构列表中位于下方的日志记录器是列表中较高位置的日志记录器的子级。例如,假定有一个名叫foo的日志记录器,则名字为foo.bar.baz,foo.bar.baz和foo.bam的日志记录器都是foo的子级。而且,所有日志记录器都是根据日志记录器的logging.getLogger(__name__)以每个模块为基础来组织你的日志记录器则将与后者完全一致。这是因为在一个模块中,__name__是该模块在Python包命名空间中的名字。

【属性6个】

1、name

这是日志记录器的名称,用于标识不同的日志记录器。

名称可以是任意字符串,通常是模块的名称。也是传给getLogger()用以获取日志记录器的值。(备注:该属性应当被视为是只读的)

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.name) # 输出:__main__

2、level

默认值:0(即NOTSET,会记录所有值)

当前Logger的日志级别,决定了哪些日志消息会被处理。

setLevel()方法设置。(注:请不要直接设置该值——应当始终使用setLever(),它检查传入的级别)

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.level) # 输出:0

logger.setLevel(10)

print(logger.level) # 输出:10

3、parent

此日志记录器的父日志记录器。它可能会根据命名空间层级结构中更高日志记录器的实例化而发生变化。(注:该值应被视为只读)

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.parent) # 输出:<RootLogger root (WARNING)>

4、propagate

默认值:True

指示是否将日志消息 传播到父Logger。如果为True,则消息会被传递到父记录器。

举例:

如果名为A.B.C的记录器的传播属性求值为真,则任何通过调用诸如logging.getLogger('A.B.C').error(...)之类的方法记录到A.B.C的事件,在第一次被传递到A.B.C上附加的处理器后,将(取决于传递该记录器的级别和过滤器设置)依次传递给附加到名为A.B,A的记录器和根记录器的所有处理器。如果A.B.C、A.B、A组成的链中,任一记录器的propagate属性设置为假,那么这将是最后一个其处理器会收到事件的记录器,此后传播在该点停止。

注:如果你将一个处理器附加到一个记录器和其一个或多个祖先记录器,它可能发出多次相同的记录。通常,您不需要将一个处理器附加到一个以上的记录器上——如果您将它附加到记录器层次结构中最高的适当记录器,则它将看到所有后代记录器记录的所有事件,前提是它们的传播设置保留为True。一种常见的方案是仅将处理器附加到根记录器,通过传播来处理其余部分。

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.propagate) # 输出:True

5、handlers

默认值:[]

一个列表,包含与该Logger关联的所有处理器(Handlers)。处理器负责将日志消息输出到不同的目标(如控制台、文件等)

直接连接到此记录器的处理程序列表实例。(注:该属性应被视为只读;通常通过addHandler()removeHandler()方法进行更改,它们使用锁来确保线程安全的操作。)

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.handlers) # 输出[]

6、disabled

默认值:False

该属性禁用对任何事件的处理。初始化程序将其设置False,只有日志配置代码才能更改。(注:该属性应当被视为是只读的)

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.disabled) # 输出:False

【方法20个】

1、debug(msg, *args, **kwargs)

在此记录器上记录DEBUG级别的消息。

msg:是消息格式字符串,

args:是用于字符串格式化操作合并到msg的参数。

(请注意,这意味着您可以在格式字符串中使用关键字以及单字典参数。)当未提供args时,不会对msg执行%格式化操作。

kwargs中会检查四个关键字参数:exc_info,stack_info,stacklevel和extra。

——exc_info的求值结果如果不为false,则它将异常信息添加到日志消息中。如果提供了一个异常元组(按照sys.exc_info()返回的格式)或一个异常实例,则它将被使用;否则,调用sys.exc_info()以获取异常信息。

第二个可选关键字参数是stack_info,默认为False。如果为True,则将堆栈信息添加到日志消息中,包括实际的日志调用。请注意,这与通过指定exc_info显示的堆栈信息不同:前者是从堆栈询问到当前线程中的日志记录调用的堆栈帧,而后者是在搜索异常处理程序时,跟踪异常而打开的堆栈帧的信息。

您可以独立于exc_info来指定stack_info,例如,即使在未引发任何异常的情况下,也可以显示如何到达代码中的特定点。堆栈帧在标题行之后打印:

——Stack(most recent call last):

这模仿了显示异常帧时所使用的Traceback(most recent call last):。

——stacklever,默认为1.如果大于1,则在为日志记录事件创建的LogRecord中计算行号和函数名时,将跳过相应数量的堆栈帧。可以在记录帮助器时使用它,以便记录的函数名称,文件名和行号不是帮助器的函数/方法的信息,而是其调用方的信息。此参数是warnings模块中的同名等效参数。

——extra,可用于传递一个字典,该字典用于用户定义的属性填充为日志事件创建的LogRecord的__dict__。然后,可以根据需要使用这些自定义的属性。例如,它们可以被合并到记录的消息中。如:

FORMAT = '%(asctime)s %(clientip)-15s %(user)-8s %(message)s'

logging.basicConfig(format=FORMAT)

d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}

logger = logging.getLogger('tcpserver')

logger.warning('Protocol problem: %s', 'connection reset', extra=d)

输出类似于

2006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

在传入到extra参数的字典的键不应与日志系统所使用4的键相冲突。

2、info(msg, *args, **kwargs)

在此记录器上记录INFO级别的消息。参数解释同debug()。

3、warning(msg, *args, **kwargs)

在此记录器上记录WARNING级别的消息。参数解释同debug()。

备注:有一个功能上与warning一致的方法warn。由于warn已被弃用,请不要使用它——改为使用warning。

4、error(msg, *args, **kwargs)

在此记录吕上记录ERROR级别的消息。参数解释同debug()。

5、critical(msg, *args, **kwargs)

在此记录吕上记录CRITICAL级别的消息。参数解释同debug()。

6、log(level, msg, *args, **kwargs)

在此记录器上记录level整数代表的级别的消息。参数解释同debug()。

7、setLever(level)

设置Logger的日志级别。日志等级小于level会被忽略。

创建记录器时,级别默认设置为NOTSET(当记录器是根记录器时,将处理所有消息;如果记录器不是根记录器,则将委托给父级)。请注意,根记录器的默认级别为WARNING

委派给父级的意思是如果一个记录器的级别设置为NOTSET,将遍历其祖先记录器,直到找到级别不是NOTSET的记录器,或者到根记录器为止。

如果发现某个父级的级别不是NOTSET,那么该父级的级别将被视为发起搜索的记录器的有效级别,并用于确定如何处理日志事件。

如果搜索到达根记录器,并且其级别为NOTSET,则将处理所有消息。否则,将使用根记录器的级别作为有效级别。

——示例:

import logging

logger = logging.getLogger(__name__)

logger.setLevel("INFO")

print(logger.isEnabledFor(20)) # 输出True

8、isEnabledFor(level)

指示此记录器是否将处理级别为level的消息。此方法首先检查由logging.disable(level)设置的模块级的级别,然后检查由getEffectiveLevel()确定的记录器的有效级别。

——示例:

import logging

logger = logging.getLogger(__name__)

print(logger.isEnabledFor(10)) # 输出:False

9、getEffectiveLevel()

指示此记录器的有效级别。如果通过setLevel()设置了除NOTSET以外的值,则返回该值。否则,将层次结构遍历到根,直到找到除NOTSET以外的其他值,然后返回该值。返回的值是一个整数,通常为logging.DEBUGlogging.INFO等等。

——示例:

import logging

logger = logging.getLogger(__name__)

logger.setLevel("CRITICAL")

print(logger.getEffectiveLevel()) #输出:50

10、getChild(suffix)

返回由后缀确定的该记录器的后代记录器。因此,logging.getLogger('abc').getChild('def.ghi')与logging.getLogger('abc.def.ghi')将返回相同的记录器。这是一个便捷方法,当使用如__name__而不是字符串字面值命名父记录器时很有用。

Added in version 3.2

11、getChildren()

返回由该日志记录器的直接下级日志记录器组成的集合。举例来说logging.getLogger().getChildren()将返回包含名为foo和bar的日志记录器的集合,但名为foo.bar的日志记录器则不会包括在集合中。类似地,logging.getLogger('foo').getChildren()将返回包括名为foo.bar的日志记录器的集合,但不会包括名为foo.bar.baz的日志记录器。

12、exception(msg, *args, **kwargs)

在此记录器上记录ERROR级别的消息。参数解释同debug()。异常信息将添加到日志消息中。仅应从异常处理程序中调用此方法。

13、addFilter(filter)

将指定的过滤器filter添加到此记录器。

14、removeFilter(filter)

将此记录器的过滤器应用于记录,如果记录能被处理则返回True。过滤器会被依次使用,直到其中一个返回假值为止。如果它们都不返回假值,则记录将被处理(传递给处理器)。如果返回任一为假值,则不会对该记录做进一步处理。

15、addHandler(hdlr)

将一个处理器添加到Logger。

16、removeHanler(hdlr)

从此记录器删除指定的处理器hdlr。

17、findCaller(stack_info=False,stacklever=1)

查找调用源的文件名和行号,以文件名,行号,函数名称和堆栈信息4元素元组的形式返回。堆栈信息将返回None,除非stack_info为True。

stacklevel参数用于调用debug()和其他API。如果大于1,则多余部分将用于跳过堆栈帧,然后再确定要返回的值。当从帮助器/包装器代码调用日志记录API时,这通常很有用,以便事件日志中的信息不是来自帮助器/包装器代码,而是来自调用它的代码。

18、handle(record)

通过将记录传递给与此记录器及其祖先关联的所有处理器来处理(直到某个propagate值为False)。此方法用于从套接字接收的未序列化的以及在本地创建的记录。使用filter()进行记录器级别过滤。

19、makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)

这是一种工厂方法,可以在子类中对其进行重写以创建专门的LogRecord实例。

20、hasHandlers()

检查此记录器是否配置了任何处理器。通过在此记录器及其记录器层次结构中的父级中查找处理器完成此操作。如果找到处理器则返回True,否则返回False。只要找到“propagate”设置为假值的记录器,该方法就会停止搜索层次结构——其将是最后一个检查处理器是否存在的记录器。

在3.7版本发生变更:现在可以对处理器进行序列化和反序列化。

——示例:展示如何使用Logger类的属性及方法

import logging

# 创建一个Logger实例

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 创建控制台处理器

console_handler = logging.StreamHandler()

console_handler.setLevel(logging.DEBUG)

# 创建格式器并将其添加到处理器

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

console_handler.setFormatter(formatter)

# 将处理器添加到logger

logger.addHandler(console_handler)

# 记录不同级别的日志

logger.debug('这是调试信息')

logger.info('这是信息')

logger.warning('这是警告信息')

logger.error('这是错误信息')

logger.critical('这是严重错误')

# 查看Logger的属性

print(f"Logger名称: {logger.name}")

print(f"Logger级别: {logger.level}")

print(f"Logger处理器: {logger.handlers}")

print(f"Logger传播: {logger.propagate}")

# 创建子Logger

child_logger = logger.getChild('child_logger')

child_logger.info('这是子Logger的信息')

输出:

2024-10-05 16:00:15,243 - my_logger - DEBUG - 这是调试信息

2024-10-05 16:00:15,244 - my_logger - INFO - 这是信息

2024-10-05 16:00:15,244 - my_logger - WARNING - 这是警告信息

2024-10-05 16:00:15,244 - my_logger - ERROR - 这是错误信息

2024-10-05 16:00:15,244 - my_logger - CRITICAL - 这是严重错误

Logger名称: my_logger

Logger级别: 10

Logger处理器: [<StreamHandler <stderr> (DEBUG)>]

Logger传播: True

2024-10-05 16:00:15,249 - my_logger.child_logger - INFO - 这是子Logger的信息


日志级别(6个)

1、logging.NOTSET

数值0,表示将查询上级日志记录器以确定生效级别。如仍被解析为NOTSET。则记录所有事件。

2、logging.DEBUG

数值10,详细的信息,通常只有试图诊断问题的开发人员才会感兴趣。

3、logging.INFO

数值20,确认程序按预期运行。

4、loging.WARNING

数值30,表示发生了意外情况,或近期有可能发生问题。软件仍会按预期工作。

5、logging.ERROR

数值40,由于严重的问题,程序的某些功能已经不能正常执行。

6、logging.CRITICAL

数值50,严重的错误,表明程序已不能继续执行。


二、处理器对象

class logging.Handler

处理器具有以下属性和方法。请注意Handler不可直接实例化;该类是被作为更有用的子类的基类。不过,子类中的__init__()方法需要调用Handler.__init__()。

【方法15个】

1、__init__(level=NOTSET)

初始化Handler实例时,需要设置它的级别,将过滤列表置为空,并且创建锁(通过createLock())来序列化对I/O的访问。

2、createLock()

初始化一个线程锁,用来序列化对底层的I/O功能的访问。底层的I/O功能可能不是纯种安全的。

3、acquire()

获取由createLock()创建的线程锁。

4、release()

释放由acquire()获取的线程锁。

5、setLevel(level)

设置处理器的日志级别。日志级别小于level将被忽略。

创建处理器时,日志级别被设置为NOTSET(所有的消息都会被处理)

在3.2版本发生变更:level形参现在接受像'INFO'这样的字符串形式的级别表达方式,也可以使用像INFO这样的整数常量。

6、setFormatter(fmt)

设置处理器的格式器。

7、addFilter(filter)

将指定的过滤器filter添加到此处理器。

8、removeFilter(filter)

从此处理器中删除指定的过滤器filter。

9、filter(record)

将此处理器的过滤器应用于记录,在要处理记录时返回True。依次查询过滤器,直到其中一个返回假值为止。如果它们都不返回假值,则将发出记录。如果返回一个假值,则处理器将不会发出记录。

10、flush()

确保所有日志记录从缓存输出。此版本不执行任何操作,并且应由子类实现。

11、close()

回收处理器使用的所有资源。此版本不输出,但从内部处理器列表中删除处理器,内部处理器在shutdown()被调用时关闭。子类应确保从重写的close()方法中调用此方法。

12、handle(record)

将已添加到处理器的过滤器过滤后,有条件地发出指定的日志记录。用获取/释放I/O线程锁包装了记录的实际发出行为。

13、handleError(record)

此方法应当在emit()调用期间遇到异常时从处理器中调用。如果模块级属性raiseExceptions为False,则异常将被静默地忽略。这是大多数情况下日志系统所需要的——大多数用户不会关心日志系统中的错误,他们对应用程序错误更感兴趣。但是,你可以根据需要将其替换为自定义处理器。指定的记录是发生异常时正在处理的记录。(raiseExceptions的默认值是True,因为这在开发过程中更有用处)

14、format(record)

如果设置了格式器则用其对记录进行格式化。否则,使用模块的默认格式器。

15、emit(record)

将日志记录(LogRecord对象)发送到处理器的目标。这个方法需要被子类实现,因此这里直接引发NotImplementeError异常。

警告:

 此方法会在获得一个处理器层级的锁之后被调用,在此方法返回之后锁将被释放。 当你重写此方法时,请注意在调用任何可能执行锁定操作的日志记录 API 的其他部分的方法时务必小心谨慎,因为这可能会导致死锁。 具体来说:

  1. 日志记录配置 API 会获取模块层级锁,然后还会在配置处理器时获取处理器层级锁。
  2. 许多日志记录 API 都会锁定模块级锁。 如果这样的 API 在此方法中被调用,则它可能会在另一个线程执行配置调用时导致死锁,因为那个线程将试图在处理器级锁 之前 获取模块级锁,而这个线程将试图在处理器级锁 之后 获取模块级锁(因为在此方法中,处理器级锁已经被获取了)。

——示例1:如何创建自定义的handler类,并使用它来处理日志记录

import logging

# 自定义处理器

class CustomHandler(logging.Handler):

    def emit(self, record):

        # 格式化日志消息

        log_entry = self.format(record)

        # 输出到控制台

        print(f"自定义处理器输出: {log_entry}")

# 配置日志记录器

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 创建自定义处理器实例

custom_handler = CustomHandler()

custom_handler.setLevel(logging.DEBUG)

# 创建格式器并将其添加到处理器

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

custom_handler.setFormatter(formatter)

# 将自定义处理器添加到logger

logger.addHandler(custom_handler)

# 记录不同级别的日志

logger.debug('这是调试信息')

logger.info('这是信息')

logger.warning('这是警告信息')

logger.error('这是错误信息')

logger.critical('这是严重错误')

输出:

自定义处理器输出: 2024-10-05 16:40:50,435 - my_logger - DEBUG - 这是调试信息

自定义处理器输出: 2024-10-05 16:40:50,435 - my_logger - INFO - 这是信息

自定义处理器输出: 2024-10-05 16:40:50,435 - my_logger - WARNING - 这是警告信息

自定义处理器输出: 2024-10-05 16:40:50,435 - my_logger - ERROR - 这是错误信息

自定义处理器输出: 2024-10-05 16:40:50,435 - my_logger - CRITICAL - 这是严重错误


三、格式器对象

class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)

格式器对象负责将一个LogRecord转换为可供人类或外部系统解读的输出字符串。

【参数(5个)】

1、fmt(str)

字符串类型,定义日志消息的格式。

用于日志记录整体输出的给定style形式的格式字符串。可用的映射键将从LogRecord对象的LogRecord属性中提取。如果未指定,则将使用'%(message)s',即已记录的日志消息。

2、datefmt(str)

字符串类型,定义日期和时间的格式。

用于日志记录输出的日期/时间部分的给定style形式的格式字符串。如果未指定,则将使用formatTime()中描述的默认值。

3、style(str)

字符串类型,指定格式化字符串的格式。

可以是'%','{'或'$'之一并决定格式字符串将如何与数据合并:使用printf风格的字符串格式化(%),str.format()({)或string.Template($)之一。这将只应用于fmt和datefmt(例如'%(message)s')或'{message}'),而不会应用于传给日志记录方法的实际日志消息。但是,也存在其他方式可以为日志消息使用{和$格式化。

4、validate(bool)

指示是否验证格式字符串的有效性。如为False,则不会检查格式字符串的有效性。

如果为True(默认值),则不正确或不匹配的fmt和style将引发ValueError;例如logging.Formatter('%(asctime)s - %(message)s', style='{')

5、defaults(dict[str, Any])

字典类型,提供默认值,可以在格式字符串中使用。

一个由在自定义字段中使用的默认值组成的字典。例如logging.Formatter('%(ip)s %(message)s', defaults={"ip":None})

在3.2版本发生变更:增加了style形参

在3.8版本发生变更:增加了validate形参

在3.10版本发生变更:增加了defaults形参

【方法4个】

1、format(record)

用于格式化日志记录对象record,并返回格式化后的日志消息字符串。

记录的属性字典被用作字符串格式化操作的操作数。返回结果字符串。在格式化该字典之前,会执行几个预备步骤。记录的message属性是用msg % args来计算的。如果格式化字符串包含'(asctime)',则会调用formatTime()来格式化事件时间。如果有异常信息,则使用formatException()将其格式化并添加到消息中。请注意已格式化的异常信息会缓存在exc_text属性中。在这种情况下,你必须在一个格式化器完成格式化后清空缓存的值(通过将exc_text属性设为None),以例下一个处理事件的格式化器不会使用缓存的值,而是重新计算它。

如果栈信息可用,它将被添加在异常信息之后,如有必要请使用formatStack()来转换它。

——示例:

import logging

# 创建一个日志记录器

logger = logging.getLogger('example_logger')

logger.setLevel(logging.DEBUG)

# 创建一个格式化器

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 创建一个日志记录对象

record = logger.makeRecord('example_logger', logging.INFO, '', 0, '这是一个信息', None, None)

# 格式化日志记录

formatted_message = formatter.format(record)

print(formatted_message)

输出:2024-10-05 16:52:46,239 - example_logger - INFO - 这是一个信息

2、formatTime(record, datefmt=None)

此方法应由想要使用格式化时间的格式器中的format()调用。可以在格式器中重写此方法以提供任何特定要求,但是基本行为如下:如果指定了datefmt(字符串),则将其用于time.strftime()来格式化记录的创建时间。否则,使用格式'%Y-Tm-Td %H:%M:%S,uuu',其中uuu部分是毫秒值,其他字母根据time.strftime()文档。这种时间格式的——示例为2003年1月23日 00:29:50,411。返回结果字符串。

此函数使用一个用户可配置函数将创建时间转换为元组。默认情况下,使用time.localtime();要为特定格式程序实例更改此项,请将实例的converter属性设为具有与time.localtime()或time.gmtime()相同签名的函数。要为所有格式化程序更改此项,例如当你希望所有日志时间都显示为GMT,请在Formatter类中设置converter属性。

在3.3版本发生变更:在之前版本中,默认格式是被硬编码的,例如这个例子:2010-09-06 22:39:15,292其中逗号之前的部分由strptime格式字符串('%Y-%m-%d %H:%M:%S')处理,而逗号之后的部分为毫秒值。因为strptime没有表示毫秒的占位符,毫秒值使用了另外的格式字符串来添加'%s,%03d'---这两个格式字符串代码都是硬编码在该方法中的。经过修改,这些字符串被定义为default_msec_format(用于添加毫秒值)。

在3.9版本发生变更:default_msec_format可以为None。

3、fromatException(exc_info)

用于格式化异常信息,通常在记录异常时使用。

参数

exc_info:一个包含异常信息的元组,通常是通过sys.exc_info()获取

将指定的异常信息(由sys.exc_info()返回的标准异常元组)格式化为字符串。默认实现只是使用了traceback.print_exception()。结果字符串将被返回。

——示例:如何使用logging模块来记录异常信息

import logging  

import sys  

  

# 配置日志格式(可选)  

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')  

  

def example_function():  

    try:  

        1 / 0  # 故意引发一个除以零的异常  

    except Exception as e:  

        # 使用 logging.error 并传递 exc_info=True 来自动格式化异常信息  

        logging.error("An error occurred", exc_info=True)  

  

if __name__ == "__main__":  

example_function()

输出:

2024-10-06 08:44:33,032 - ERROR - An error occurred

Traceback (most recent call last):

  File "C:\Users\Administrator\Desktop\Crawl\test.py", line 9, in example_function

    1 / 0  # 故意引发一个除以零的异常

ZeroDivisionError: division by zero

4、formatStack(stack_info)

将指定的异常堆栈信息(traceback.print_stack()返回的字符串,但移除末尾的换行符)格式化为字符串。默认实现只是返回输入值。

class logging.BufferingFormatter(linefmt=None)

适合用来在你想要格式化多条记录时进行子类化的格式化器。你可以传一个Formatter实例用来格式化每一行(每一行对应一条记录)。如果未被指定,则会使用默认的格式化器(仅输出事件消息)作为格式化器。

【方法3个】

1、formatHeader(records)

为records列表返回一个标头。基本实现只是返回空字符串。如果你想要指明特定行为则需要重写此方法,例如显示记录条数、标题或分隔行等。

2、formatFooter(records)

为records列表返回一个结束标记。基本实现只是返回空字符串。如果你想要指定特定行为则需要重写此方法,例如显示记录条数或分隔行等。

3、format(records)

为records列表返回已格式化文本。基本实现在没有记录时只是返回空字符串;在其他情况下,它将返回标头、使用行格式化器执行格式化的每行记录以及结束标记。


四、过滤器对象

过滤器(Filter)用于控制哪些日志记录可以通过特定的Logger、Handler或Filter对象进行处理。过滤器可以根据特定的条件(如日志级别、消息内容等)来决定是否记录某条日志信息。

Filters可被Handlers和Loggers用来实现比按层级提供更复杂的过滤操作。基本过滤器类只允许低于日志记录器层级结构中低于特定层级的事件。

例如,一个用'A.b'初始化的过滤器将允许'A.B','A.B.C','A.B.C.D','A.B.D'等日志记录器所记录的事件。但'A.BB','B.A.B'等则不允许。如果用空字符串初始化,则所有事件都会通过。

——作用

控制日志输出

通过设置过滤器,可以精确控制哪些日志信息被记录、哪些被忽略。这对于大型应用程序尤其重要,可以帮助减少日志的噪声。

实现复杂的过滤逻辑

可以根据自定义的条件(如特定的模块、用户ID等)来过滤日志信息。

提高性能

通过减少不必要的日志记录,可以提高应用程序的性能,尤其是在高负载的情况下。

——示例1只记录包含"重要"字符串的记录

import logging

# 创建一个自定义过滤器

class MyFilter(logging.Filter):

    def filter(self, record):

        # 只记录包含'重要'的日志消息

        return '重要' in record.msg

# 配置日志记录器

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 创建控制台处理器

console_handler = logging.StreamHandler()

console_handler.setLevel(logging.DEBUG)

# 将自定义过滤器添加到处理器

console_handler.addFilter(MyFilter())

# 创建格式器并将其添加到处理器

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

console_handler.setFormatter(formatter)

# 将处理器添加到logger

logger.addHandler(console_handler)

# 记录不同的日志信息

logger.debug('这是一个调试信息')

logger.info('这是一个重要的信息')

logger.warning('这是一个警告信息')

logger.error('这是一个重要的错误信息')

logger.critical('这是一个严重错误')

输出:

2024-10-05 14:17:37,126 - my_logger - INFO - 这是一个重要的信息

2024-10-05 14:17:37,126 - my_logger - ERROR - 这是一个重要的错误信息

class logging.Filter(name='')

返回一个Filter类的实例。如果指定了name,则它将被用来为日志记录器命名,该类及其子类将通过该过滤器允许指定事件通过。如果name为空字符串,则允许所有事件通过。

filter(record)

指定的记录是否会被写入日志?否则返回假值,是则返回真值。过滤器可以原地修改日志记录或者返回完全不同的记录实例并在该事件未来的任何处理过程中用它来替代原始日志记录。

请注意关联到处理器的过滤器会在事件由处理器发出之前被查询,而关联到日志记录器的过滤器则会在有事件被记录的任何时候(使用debug(),info()等等)在将事件发送给处理器之前被查询。这意味着由后代日志记录器生成的事件将不会被父代日志记录器的过滤器设置所过滤,除非该过滤器也已被应用于后代日志记录器。

你实际上不需要子类化Filter:你可以传入任何一个包含有相同语义的filter方法的实例。

在3.2版本发生变更:你不需要创建专门的Filter类,或使用具有filter方法的其他类:你可以使用一个函数(或其他可调用对象)作为过滤器。过滤逻辑将检查过滤器是否具有filter属性:如果有,就会将它当作是Filter并调用它的filter()方法。在其他情况下,则会将它当作是可调用对象并将记录作为唯一的形参进行调用。返回值应当与filter()的返回值相一致。

在3.12版本发生变更:现在你可以过滤器返回一个LogRecord实例来替代日志记录而不是原地修改它。这允许附加到特定Handler的过滤器在日志记录发出之前修改它,而不会对其他处理器产生附带影响。

尽管过滤器主要被用来构造比层级更复杂的规则以过滤记录,但它们可以查看由它们关联的处理器或记录器所处理的每条记录:当你想要执行统计特定记录器或处理器共处理了多少条记录,或是在所处理的LogRecord中添加、修改或移除属性这样的任务时该特性将很有用处。显然改变LogRecord时需要相当小心,但将上下文信息注入日志确实是被允许的(参见使用过滤器传递上下文信息

五、LogRecord属性

它用于封闭日志记录的信息。每当你调用Logger的日志记录方法(如debug(),info(),warning(),error(),critical())时,都会创建一个LogRecord实例。这个实例包含了关于日志事件的所有信息。

LogRecord实例是每当有日志被记录时由Logger自动创建的,并且可通过makeLogRecord()手动创建(例如根据从网络接收的已封存事件创建)

class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)

包含与被记录的事件相关的所有信息。

主要信息是在msg中args传递的,它们使用msg % args组合到一起以创建记录的message属性。

【参数9个】

1、name(str)

记录器的名称,通常是模块的名称或自定义的名称。

请注意LogRecord中的记录器名称将始终为该值,即使它可能是由附加到不同(上级)日志记录器的处理器所发出的。

2、level(int)

日志级别,表示日志的严重性。

日志记录事件的数字层级(如10表示DEBUG等等)。请注意这会转换为LogRecord的两个属性:levelno表示数字值而levelname表示对应的层级名。

3、pathname(str)

触发日志记录的源文件的完整路径。

4、lineno(int)

触发日志记录的源文件中的行号。

5、msg(Any)

日志消息的格式字符串。

事件描述消息,这可以是一个带有%形式可变数据占位符的格式字符串,或是任意对象(参见使用任意对象作为消息)

6、args(tuple|dict[str,Any])

用于格式化msg的参数。

要合并到msg参数以获得事件描述的可变数据。

7、exc_info(tuple[type[BaseException],BaseException,types.TracebackType]|None)

异常信息,通常是sys.exc_info()的返回值,用于记录异常信息。如果没有异常则为None。

8、func(str|None)

触发日志记录的函数名称(可选)

9、sinfo(str|None)

堆栈信息,用于记录异常时的堆栈跟踪信息。

一个文本字符串,表示当前线程中从堆栈底部直到日志记录调用的堆栈信息。

——示例1:展示如何使用LogRecord类来创建自定义日志记录

import logging

import sys

# 自定义日志处理器

class CustomHandler(logging.Handler):

    def emit(self, record):

        # 创建LogRecord实例

        log_record = logging.LogRecord(

            name=record.name,

            level=record.levelno,

            pathname=record.pathname,

            lineno=record.lineno,

            msg=record.msg,

            args=record.args,

            exc_info=record.exc_info,

            func=record.funcName,

            sinfo=record.stack_info

        )

        # 输出日志信息

        print(f"{log_record.levelname}: {log_record.msg} (来自 {log_record.pathname}:{log_record.lineno})")

# 配置日志记录器

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 添加自定义处理器

custom_handler = CustomHandler()

logger.addHandler(custom_handler)

# 记录日志

logger.debug('这是一个调试信息')

logger.info('这是一个信息')

logger.warning('这是一个警告信息')

logger.error('这是一个错误信息')

logger.critical('这是一个严重错误')

输出:

DEBUG: 这是一个调试信息 (来自 C:\Users\Administrator\Desktop\Crawl\test.py:31)

INFO: 这是一个信息 (来自 C:\Users\Administrator\Desktop\Crawl\test.py:32)

WARNING: 这是一个警告信息 (来自 C:\Users\Administrator\Desktop\Crawl\test.py:33)

ERROR: 这是一个错误信息 (来自 C:\Users\Administrator\Desktop\Crawl\test.py:34)

CRITICAL: 这是一个严重错误 (来自 C:\Users\Administrator\Desktop\Crawl\test.py:35)

LogRecord属性

LogRecord具有许多属性,它们大多数来自于传递给构造器的形参。(请注意LogRecord构造器开参与LogRecord属性的名称并不总是完全彼此对应的)这些属性可被用于将来自记录的数据合并到格式字符串中。下面的表格(按字母顺序)列出了属性名称、它们的含义以及相应的% -style格式字符串内占位符。

如果是使用{}-格式化(str.format()),你可以将{attrname}用作格式字符串内的占位符。如果是使用$-格式化(string.Template),则会使用${attrname}的形式。当然在这两种情况下,都应当将attrname替换为你想要使用的实际属性名称。

属性名称

格式

描述

args

此属性不需要用户进行格式化。

合并到 msg 以产生 message 的包含参数的元组,或是其中的值将被用于合并的字典(当只有一个参数且其类型为字典时)。

asctime

%(asctime)s

表示人类易读的 LogRecord 生成时间。 默认形式为 '2003-07-08 16:49:45,896' (逗号之后的数字为时间的毫秒部分)。

created

%(created)f

Time when the LogRecord was created (as returned by time.time()).

exc_info

此属性不需要用户进行格式化。

异常元组(例如 sys.exc_info)或者如未发生异常则为 None

文件名

%(filename)s

pathname 的文件名部分。

funcName

%(funcName)s

函数名包括调用日志记录.

levelname

%(levelname)s

消息文本记录级别('DEBUG''INFO''WARNING''ERROR''CRITICAL')。

levelno

%(levelno)s

消息数字的记录级别 (DEBUGINFOWARNINGERRORCRITICAL).

lineno

%(lineno)d

发出日志记录调用所在的源行号(如果可用)。

message

%(message)s

记入日志的消息,即 msg % args 的结果。 这是在发起调用 Formatter.format() 时设置的。

module

%(module)s

模块 (filename 的名称部分)。

msecs

%(msecs)d

LogRecord 被创建的时间的毫秒部分。

msg

此属性不需要用户进行格式化。

在原始日志记录调用中传入的格式字符串。 与 args 合并以产生 message,或是一个任意对象 (参见 使用任意对象作为消息)。

name

%(name)s

用于记录调用的日志记录器名称。

pathname

%(pathname)s

发出日志记录调用的源文件的完整路径名(如果可用)。

process

%(process)d

进程ID(如果可用)

processName

%(processName)s

进程名(如果可用)

relativeCreated

%(relativeCreated)d

以毫秒数表示的 LogRecord 被创建的时间,即相对于 logging 模块被加载时间的差值。

stack_info

此属性不需要用户进行格式化。

当前线程中从堆栈底部起向上直到包括日志记录调用并引发创建当前记录堆栈帧创建的堆栈帧信息(如果可用)。

thread

%(thread)d

线程ID(如果可用)

threadName

%(threadName)s

线程名(如果可用)

taskName

%(taskName)s

asyncio.Task 名称(如果可用)。

在 3.1 版本发生变更: 添加了 processName

在 3.12 版本发生变更: 添加了 taskName

六、LoggerAdapter对象

LoggerAdapter实例会被用来方便地将上下文信息传入日志记录调用。

class logging.LoggerAdapter(logger,extra)

它允许你在日志消息中动态地插入上下文信息,而无需修改现有的日志调用。这个类通过包装一个已有的logger实例,并在日志消息传递给它之前对消息和/或关键字参数进行处理来实现这一功能。

返回一个LoggerAdapter实例,该实例已用底层Logger实例和类似字典的对象初始化。

参数

logger:这是一个Logger对象,通常是通过logging.getLogger(name)创建的。这是实际记录日志的基础。

extra:这是一个字典,包含额外的上下文信息。这个字典中的键值对可以在日志消息中使用,通常用于提供额外的上下文信息,比如用户ID、请求ID等。

——示例1

import logging

# 创建一个Logger对象

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 创建一个控制台处理器并设置级别

ch = logging.StreamHandler()

ch.setLevel(logging.DEBUG)

# 创建一个格式化器并将其添加到处理器

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s - User ID:%(user_id)s')

ch.setFormatter(formatter)

# 将处理器添加到Logger

logger.addHandler(ch)

# 创建一个LoggerAdapter

extra = {'user_id': '12345'}

adapter = logging.LoggerAdapter(logger, extra)

# 使用LoggerAdapter记录日志

adapter.debug('This is a debug message.')

adapter.info('This is an info message.')

adapter.warning('This is a warning message.')

adapter.error('This is an error message.')

adapter.critical('This is a critical message.')

输出

2024-10-07 15:17:45,753 - my_logger - DEBUG - This is a debug message. - User ID:12345

2024-10-07 15:17:45,753 - my_logger - INFO - This is an info message. - User ID:12345

2024-10-07 15:17:45,753 - my_logger - WARNING - This is a warning message. - User ID:12345

2024-10-07 15:17:45,753 - my_logger - ERROR - This is an error message. - User ID:12345

2024-10-07 15:17:45,753 - my_logger - CRITICAL - This is a critical message. - User ID:12345

【方法】

process(msg,kwargs)

用于修改传递给日志记录调用的消息和关键字参数,以便插入上下文信息。

这个方法的主要作用是将构造器中以extra形式传递的对象添加 到日志记录的关键字参数中。

参数

msg:这是传递给日志记录调用的原始消息。

kwargs:这是一个字典,包含传递给日志记录调用的关键字参数。

返回值

返回一个元组(msg, kwargs),其中包含(可能经过修改的)传入参数。具体来说,kwargs中将包含extra字典中的内容,使用'extra'作为键名。

——示例1

import logging  

from logging import LoggerAdapter  

  

class CustomLoggerAdapter(LoggerAdapter):  

    def __init__(self, logger, extra={}):  

        super().__init__(logger, extra)  

  

    def process(self, msg, kwargs):  

        # 在这里添加自定义的上下文信息  

        if 'extra_info' not in kwargs:  

            kwargs['extra_info'] = self.extra.get('extra_info', 'No extra info provided')  

        # 可以对消息本身进行修改,这里简单地在消息末尾添加了额外的信息  

        msg = f"{msg} [Extra Info: {kwargs['extra_info']}]"  

        return msg, kwargs  

  

# 创建一个Logger实例  

logger = logging.getLogger('my_logger')  

# 创建一个自定义的LoggerAdapter实例,并传入一个包含额外信息的字典  

extra_info = {'extra_info': 'This is some extra context'}  

custom_logger = CustomLoggerAdapter(logger, extra_info)  

  

# 使用LoggerAdapter实例进行日志记录  

custom_logger.info('This is a test log message')

manager

manager属性是一个委托属性,它代理到被包装的logger实例的manager属性上。

_log方法

_log方法是一个内部方法,通常不建议直接调用它。它是Logger类的一个受保护方法,用于执行的日志记录操作。

七、线程安全

logging模块的目标是使客户端不必执行任何特殊操作即可确保线程安全。它通过线程锁来达成这个目标;用一个锁来序列化对模块共享数据的访问,并且每个处理程序也会创建一个锁来序列化对其下层I/O的访问。

如果你要使用signal模块来实现异步信息处理程序,则可能无法在这些处理程序中使用logging。这是因为threading模块中的锁实现并非总是可重入的,所以无法从此信停业处理程序发起调用。

八、模块级函数(20个)

在上述的类之外,还有一些模块级函数。

1、logging.getLogger(name=None)

工厂方法,用于获取一个Logger实例。

getLogger()方法是logging模块中获取Logger实例的主要方式。通过合理使用名称参数,可以创建和管理不同的日志记录器,例于在大型应用程序中进行日志记录和调试。

返回一个由name指定名称的日志记录器,或者如果name为None则返回层级结构中的根日志记录器。如果指定了name,它通常是以点号分隔的带层级结构的名称如'a','a.b'。这些名称的选择完全取决于使用logging的开发者,不过就如在记录器对象中提到的那样建议使用__name__,除非你有不这样做的特别理由。

所有用给定的name对该函数的调用都将返回相同的日志记录器实例。这意味着日志记录器实例不需要在应用的各部分间传递。

——参数

name(可选):

这是一个字符串,用于指定Logger的名称。名称可以是任意字符串,通常用于标识不同的日志记录器。通过名称,你可以创建层次结构的日志记录器。例如,'myapp'和'myapp.module1'可以表示同一个应用程序的不同模块。

返回值

getLogger()返回一个Logger实例。如果指定的名称已经存在一个Logger实例,则返回该实例;如果不存在,则创建一个新的实例。

——示例1:获取默认Logger

import logging

import sys

# 配置记录器,使在控制台输出

logging.basicConfig(stream=sys.stdout)

# 获取默认的logger

default_logger = logging.getLogger()

default_logger.setLevel(logging.DEBUG)

default_logger.debug('这是默认logger的调试信息')

输出:DEBUG:root:这是默认logger的调试信息

——示例2:获取命名Logger

import logging

import sys

logging.basicConfig(stream=sys.stdout)

# 获取一个名为'my_logger'的logger

my_logger = logging.getLogger('my_logger')

my_logger.setLevel(logging.INFO)

my_logger.info('这是my_logger的信息')

输出:INFO:my_logger:这是my_logger的信息

——示例3:获取层次结构Logger

import logging

import sys

logging.basicConfig(stream=sys.stdout)

# 获取一个名为'myapp'的logger

app_logger = logging.getLogger('myapp')

app_logger.setLevel(logging.WARNING)

# 获取一个名为'myapp.module1'的logger

module_logger = logging.getLogger('myapp.module1')

module_logger.setLevel(logging.DEBUG)

app_logger.warning('这是myapp的警告信息')

module_logger.debug('这是myapp.module1的调试信息')

输出:

WARNING:myapp:这是myapp的警告信息

DEBUG:myapp.module1:这是myapp.module1的调试信息

注意事项

Logger的层次结构:Logger名称可以用点(.)分隔,形成层次结构。例如,'myapp'是'myapp.module1'的父logger。父logger的日志级别会影响子logger的行为。

默认Logger:如果不传递参数,getLogger()将返回一个默认的logger实例。这个logger的名称为空字符串。

线程安全:getLogger()是线程安全的,可以在多线程环境中安全使用。

——示例4

import sys

import logging

# 获取日志记录器实例

logger = logging.getLogger(__name__)

# 进行基础配置

logging.basicConfig(

stream=sys.stdout, # 流对象

level=logging.DEBUG, #日志级别

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 输出格式

datefmt='%Y-%m-%d %H:%M:%S' # 设置日期时间格式

)

logger.info('一个信息')

输出:2024-10-07 21:23:36 - __main__ - INFO - 一个信息

2、logging.basicConfig(**kwargs)

用于配置日志记录的基本设置。它可以调协日志的级别、格式、处理器等,通常在程序的开始部分调用。

通过使用默认的Formatter(格式器)创建一个StreamHandler(流处理器)并将其加入根日志记录器来为日志记录系统执行基本配置。如果没有为根日志记录器定义处理器则debug(),info(),warning(),error()和critical()等函数将自动调用basicConfig()

如果根日志记录器已配置了处理器则此函数将不执行任何操作,除非关键字参数force被设为True。

备注:此函数应当在其他线程启动之前从主纯种被调用。在2.7.1和3.2之前的Python版本中,如果此函数从多个纯种被调用,一个处理器(在极少的情况下)有可能被多次加入根日志记录器,导致非预期的结果例如日志中的消息出现重复。

——可用参数

filename

指定日志文件的名称。指定了该参数,日志将写入到文件中,而不是输出到控制台。

使用指定的文件名创建一个FileHandler,而不是StreamHandler。

filemode

指定日志文件的打开模式。

即filename创建的日志文件的写入模式。如果指定了filename,则用此模式打开该文件。默认模式为'a'追加(可用模式:w:覆盖写入、a:追加)。

例:filemode='w'

format

设置日志消息的输出格式。

——示例:format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

常用占位符:

%(name)s:Logger的名字

%(levelno)s:日志级别的数值

%(pathname)s:调用日志记录函数的源文件的完整路径名

%(module)s:调用日志记录函数的模块名

%(funcName)s:调用日志记录函数的函数名

%(lineno)d:调用日志记录函数的语句所在源代码的行号

%(created)f:当前时间,用UNIX时间的浮点数表示

%(relativeCreated)d:日志事件发生的时间相对于logging模块加载时间的毫秒数(整数)

%(asctime)s:人类可读的时间表示,默认格式为“2024-09-22 23:33:45,896”(逗号后面是毫秒)

%(thread)d:线程ID。可能没有

%(threadName)s:线程名。可能没有

%(process)d:进程ID

%(message)s:用户输出的消息

datefmt

设置日期/时间的格式,与time.strftime()所接受的格式相同。

——示例:

# myapp.py

import logging

import sys

logger = logging.getLogger(__name__)

def main():

    logging.basicConfig(level=logging.INFO,stream=sys.stdout,

     format='%(asctime)s - %(pathname)s - %(levelname)s - %(message)s',

     datefmt='%Y-%m-%d %H:%M:%S')

    logger.info('Started')

    logger.info('Finished')

if __name__ == '__main__':

    main()

输出:

2024-09-22 23:41:27 - C:\Users\hyf\Desktop\Python二级\test.py - INFO - Started

2024-09-22 23:41:27 - C:\Users\hyf\Desktop\Python二级\test.py - INFO - Finished

style

如果指定了format,将为格式字符串使用此风格。'%','{'或'$'分别对应于printf风格str.format()string.Template。默认为'%'。

注:printf风格的字符串格式化

字符串对象有一个独特的内置运算:%去处符(取模)。这也被称为字符串formattinginterpolation运算符。如果给定format % values(其中format是字符串),format中%的转换规范将被values中的零个或多个元素替换。其效果类似于在C语言中使用sprintf()。

如果format要求一个单独参数,则values可以为一个非元组对象。否则的话,values必须或者是一个包含项数与格式字符串中指定的转换符项数相同的元组,或者是一个单独的映射对象(如字典)。

1、'%'字符,用于标记转换符的起始

2、映射键(可选),由加圆括号的字符序列组成(例如(somename))

3、转换旗标(可选),用于影响某些转换类型的结果

4、最小字段宽度(可选)。如果指定为'''(星号),则实际宽度会从values元组的下一元素中读取,要转换的对象则为最小字段宽度和可选的精度之后的元素。

5、精度(可选),以在'.'(点号)之后加精度值的形式给出。如果指定为'''(星号),则实际精度会从values元组的下元素中读取,要转换的对象则为精度之后的元素。

6、长度修饰符(可选)

7、转换类型。

例:

    print('%(language)s has %(number).3f and %(du)04d quote types.' % #.3f表示保留3位小数

      {'language': "Python", "number": 2.155555,"du":5,}) #04d表示整数宽度最少为4位

输出:

Python has 2.156 and 0005 quote types.

level

设置日志记录的最低级别。——示例:level=logging.DEBUG

stream

指定一个流对象(如sys.stdout或sys.stderr),用于输出日志。

使用指定的流初始化StreamHandler。请注意此参数与filename不兼容——如果两者同时存在,则会引发ValueError。

StreamHandler类位于核心logging包,它可将日志记录发送到数据流例如sys.stdout,sys,stderr或任何文件型对象(即任何支持write()和flush()方法的对象)。

例:

# myapp.py

import logging

import sys

logger = logging.getLogger(__name__)

def main():

    logging.basicConfig(level=logging.INFO,stream=sys.stderr,

     format='%(asctime)s - %(pathname)s - %(levelname)s - %(message)s',

     datefmt='%Y-%m-%d %H:%M:%S')

    logger.info('Started')

    logger.info('Finished')

if __name__ == '__main__':

main()

控制台输出:

2024-09-23 00:23:15 - C:\Users\hyf\Desktop\Python二级\test.py - INFO - Started

2024-09-23 00:23:15 - C:\Users\hyf\Desktop\Python二级\test.py - INFO - Finished

handlers

指定一个处理器列表,用于自定义日志处理。可以使用logging.StreamHandler、logging.Filehandler等。

如果指定,这应为一个包含要加入根日志记录器的已创建处理器的可迭代对象。任何尚未设置格式描述符的处理器将被设置为在此函数中创建的默认格式描述符。请注意此参数与fimename或stream不兼容(引发ValueError错误)

——示例:

import logging

# 创建一个文件处理器

file_handler = logging.FileHandler('app.log')

file_handler.setLevel(logging.INFO)  # 设置处理器的日志级别

# 创建一个控制台处理器

console_handler = logging.StreamHandler()

console_handler.setLevel(logging.ERROR)  # 设置处理器的日志级别

# 创建一个格式化器

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

# 将格式化器添加到处理器

file_handler.setFormatter(formatter)

console_handler.setFormatter(formatter)

# 配置基本的日志设置

logging.basicConfig(level=logging.DEBUG, handlers=[file_handler, console_handler])

# 记录一些日志

logging.debug('这是调试信息')

logging.info('这是普通信息')

logging.warning('这是警告信息')

logging.error('这是错误信息')

logging.critical('这是严重错误信息')

force

默认值:False,如果将此关键字参数指定为true,则在执行其他参数指定过完前,将移除并关闭附加到根记录器的所有现有处理器。

force参数用于强制重新配置日志系统。

如果设置为True,则会强制重新配置日志系统,即使它已经被配置过。这意味着之前的配置将被覆盖。如果没有设置force参数为True,则后续重新配置的basicConfig参数不会生效,只有第一次的有效。

——示例:

import logging

# 第一次配置日志

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

logging.info('第一次配置的日志信息')

# 再次配置日志,使用force=True

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', force=True)

logging.debug('第二次配置的日志信息')

# 记录不同级别的日志

logging.info('这是信息级别的日志')

logging.debug('这是调试级别的日志')

encoding

默认值:None,如果此关键字参数与filename一同被指定,则其值会在创建FileHandler时被使用,因而也会在打出输出文件时被使用。

encoding参数用于指定日志文件的编码格式。当你将日志输出到文件时,使用encoding参数可以确保日志文件以正确的字符编码保存,这对于处理非ASCII字符(如中文)非常重要。

——示例:

import logging

# 配置日志,指定文件编码为utf-8

logging.basicConfig(

    filename='app.log',

    level=logging.INFO,

    format='%(asctime)s - %(levelname)s - %(message)s',

    encoding='GB2312'  # 指定编码格式

)

# 记录一些日志信息

logging.info('这是信息级别的日志')

logging.error('这是错误级别的日志')

errors

如果此关键字参数与filename一同被指定,则其值会在创建FileHandler时被使用,因而也会在打开输出文件时被使用。如果未指定,则会使用值'backslashreplace'。请注意如果指定为None,它将被原样传给open(),这意味着它将会当作传入'errors'一样处理。

errors参数用于指定在处理日志文件时遇到编码错误时的行为。这个参数在设置encoding参数时特别有用,因为它可以帮助你控制如何处理无法编码的字符。

常用值:

'strict':默认值,遇到编码错误时引发UnicodeEncodeError

'ignore':忽略无法编码的字符

'replace':用替代字符(通常是?)替换无法编码的字符

'backslashreplace':用Python的Unicode转义序列替换无法编码的字符

——示例:

import logging

# 配置日志,指定文件编码为utf-8,并设置错误处理策略

logging.basicConfig(

    filename='app.log',

    level=logging.INFO,

    format='%(asctime)s - %(levelname)s - %(message)s',

    encoding='utf-8',

    errors='replace'  # 设置错误处理策略

)

# 记录一些日志信息,包括可能导致编码错误的内容

logging.info('这是信息级别的日志')

logging.info('包含特殊字符: �¥')  # 可能会导致编码问题的字符

logging.error('这是错误级别的日志')

版本变化

3.2版本:增加了style参数

3.3版本:增加了handlers参数。

3.8版本:增加了force参数

3.9版本:增加了encoding和errors参数

——示例:

import logging

# 配置基本的日志设置

logging.basicConfig(

    level=logging.DEBUG,

    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',

    datefmt='%Y-%m-%d %H:%M:%S',

    filename='app.log',

    filemode='w'

)

# 创建一个logger实例

logger = logging.getLogger('my_logger')

# 记录不同级别的日志

logger.debug('这是调试信息')

logger.info('这是信息')

logger.warning('这是警告信息')

logger.error('这是错误信息')

logger.critical('这是严重错误')

输出

运行上述代码后,所有日志信息将被写入到app.log文件中,内容类似于:

2024-10-05 16:25:39 - my_logger - DEBUG - 这是调试信息

2024-10-05 16:25:39 - my_logger - INFO - 这是信息

2024-10-05 16:25:39 - my_logger - WARNING - 这是警告信息

2024-10-05 16:25:39 - my_logger - ERROR - 这是错误信息

2024-10-05 16:25:39 - my_logger - CRITICAL - 这是严重错误

3、logging.debug(msg,*args,**kwargs)

这是在根日志记录器上调用Logger.debug()的便捷函数。其参数的处理方式与该方法中的描述完全一致。唯一的区别在于如果根日志记录器没有处理器,则在根日志记录器上调用debug之前会先调用basicConfig()。

对于非常简短的脚本或logging功能的快速演示,debug和其他模块级函数可能会很方便。不过,大多数程序都会想要仔细和显式地控制日志记录配置,所以应当更倾向于创建一个模块级的日志记录器并在其上调用Logger.debug()(或其他特定级别的方法)。

——示例1

import logging

# 进行基础配置

import sys

logging.basicConfig(

stream=sys.stdout, # 流对象

level=logging.DEBUG, #日志级别

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 输出格式

datefmt='%Y-%m-%d %H:%M:%S' # 设置日期时间格式

)

logging.debug('some msg')

输出:2024-10-07 21:45:49 - root - DEBUG - some msg

4、logging.info(msg,*args,**kwargs)

5、logging.warning(msg,*args,**kwargs)

6、logging.error(msg,*args,**kwargs)

7、logging.critical(msg,*args,**kwargs)

8、logging.exception(msg,*args,**kwargs)

以上几个方法和logging.debug使用方法一致。

9、logging.log(level,msg,*args,**kwargs)

在根日志记录器上记录一条level级别的消息。其他参数与行为均与debug()相同。

10、logging.disable(level=CRITICAL)

为所有日志记录器提供重写的级别level,其优先级高于日志记录器自己的级别。当需要临时限制整个应用程序中的日志记录输出时,此功能会很有用。它的效果是禁用所有重要程度为level及以下的日志记录调用,因此如果你附带INFO值调用它,则所有INFO和DEBUG事件就会被丢弃,而重要程序为WARNING以及上的事件将根据日志记录器的当前有效级别来处理。如果logging.disable(logging.NOTSET)被调用,它将移除这个重写的级别,因此日志记录输出会再次取决于单个日志记录器的有效级别。

请注意如果你定义了任何高于CRITICAL的自定义日志级别(并不建议这样做),你就将无法沿用level形参的默认值,而必须显式地提供适当的值。

在3.7版本发生变更:level形参默认级别为CRITICAL。

11、logging.getLoggerClass()

作用

用于获取当前日志记录器类的引用。这个函数的主要作用是允许用户查看或更改日志记录器的类,以便自定义日志记录器的行为。

返回标准的Logger类,或是最近传给setLoggerClass()的类。此函数可以从一个新的类定义中调用,以确保安装自定义的Logger类不会撤销其他代码已经应用的自定义操作。例如:

class MyLogger(logging.getLoggerClass()):

# ... 在这里重写行为

——示例1:获取当前日志记录器的类

import logging

logger_class = logging.getLoggerClass()

print(logger_class)  # 输出: <class 'logging.Logger'>

——示例2:自定义一个日志记录器类,并添加自定义方法,使用setLoggerClass()方法将其设置为新的日志记录器类

import logging

class MyLogger(logging.Logger):

    def my_custom_method(self):

        print("This is a custom method in MyLogger.")

# 设置自定义日志记录器类

logging.setLoggerClass(MyLogger)

# 创建一个新的日志记录器实例

my_logger = logging.getLogger('my_custom_logger')

my_logger.my_custom_method()  # 调用自定义方法

输出:This is a custom method in MyLogger.

logging.setLoggerClass(logging.Logger) # 恢复默认日志记录器的类

12、logging.getLogRecordFactory()

返回值

返回当前的日志记录工厂函数。默认情况下,这个工厂函数是logging.LogRecord类的构造函数。

作用

这个函数的主要作用是允许用户查看或自定义日志记录对象的创建方式。自定义的工厂函数会替代默认的工厂函数,用于创建新的日志记录对象。

返回一个被用来创建LogRecord的可调用对象。

Added in version 3.2:此函数setLogRecordFactory()一起提供,以允许开发者对表示日志记录事件的LogrRecord的构造有更好的控制。

——示例1:获取当前的日志记录工厂函数

import logging

log_record_factory = logging.getLogRecordFactory()

print(log_record_factory)  # 输出: <class 'logging.LogRecord'>

输出:<class 'logging.LogRecord'>

——示例2:自定义日志记录工厂函数

import logging

def custom_log_record_factory(*args, **kwargs):

    # 创建一个自定义的 LogRecord 对象

    record = logging.LogRecord(*args, **kwargs)

    record.custom_attribute = 'This is a custom attribute'

    return record

# 设置自定义日志记录工厂

logging.setLogRecordFactory(custom_log_record_factory)

# 创建一个日志记录器并记录日志

logger = logging.getLogger('my_logger')

logger.warning('This is a warning message')

# 获取最近的日志记录

for handler in logger.handlers:

handler.flush()

输出:This is a warning message

13、logging.addLevelName(level,levelName)

作用

主要作用是允许开发者定义新的日志级别或为现有的日志级别指定更具描述性的名称。

参数

level:一个整数,表示日志级别的数值。这个数值应该是一个有效的日志级别,如DEBUG、INFO...,也可以是自定义的级别。

levelName:一个字符串,表示要为该级别添加的名称。这个名称将用于日志输出中,以便更好地描述日志的性质。

在一个内部字典中关联级别level与文本levelName,该字典会被用来将数字级别映射为文本表示形式,例如在Formatter格式化消息的时候。此函数也可被用来定义你自己的级别。唯一的限制是自定义的所有级别必须使用此函数来注册,级别值必须为正整数并且其应随严重程度而递增。

——示例1:添加自定义日志级别

import logging

# 进行基础配置

import sys

logging.basicConfig(

stream=sys.stdout, # 流对象

level=logging.DEBUG, #日志级别

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 输出格式

datefmt='%Y-%m-%d %H:%M:%S' # 设置日期时间格式

)

# 添加自定义日志级别

logging.addLevelName(5, "TRACE")

# 创建一个日志记录器

logger = logging.getLogger('my_logger')

logger.setLevel(5)  # 设置日志级别为 TRACE

# 使用自定义日志级别记录日志

logger.log(5, 'This is a trace message')

输出:2024-10-07 22:00:16 - my_logger - TRACE - This is a trace message

——示例2:为现有日志级别添加名称

import logging

# 为 WARNING 级别添加新的名称

logging.addLevelName(logging.WARNING, "ALERT")

# 创建一个日志记录器

logger = logging.getLogger('my_logger')

# 记录一个警告日志

logger.warning('This is a warning message')  # 输出: ALERT: This is a warning message

输出:2024-10-07 22:01:23 - my_logger - ALERT - This is a warning message

——示例3:查看所有日志级别名称

print(logging.getLevelName(5))  # 输出: TRACE

print(logging.getLevelName(logging.WARNING))  # 输出: ALERT

14、logging.getLevelNamesMapping()

作用

用于获取当前日志级别名称与其对应的整数级别之间的映射关系。这个函数的主要作用是提供一个字典,包含所有已定义的日志级别及其名称,方便开发者查看和使用。

返回值

返回一个字典,其中键是日志级别的名称(字符串),值是对应的日志级别数值(整数)。这个字典包括标准日志级别以及通过addLevelName()添加的自定义日志级别。

Added in version 3.11

——示例1:获取当前日志级别与数值映射

import logging

# 获取日志级别名称与数值的映射

level_mapping = logging.getLevelNamesMapping()

print(level_mapping)

输出:

{

    'CRITICAL': 50,

    'ERROR': 40,

    'WARNING': 30,

    'INFO': 20,

    'DEBUG': 10,

    'NOTSET': 0

}

15、logging.getLevelName(level)

返回日志记录级别level的字符串表示。

——示例1:获取标准日志级别名称

import logging

print(logging.getLevelName(logging.DEBUG))    # 输出: DEBUG

print(logging.getLevelName(logging.INFO))     # 输出: INFO

print(logging.getLevelName(logging.WARNING))   # 输出: WARNING

print(logging.getLevelName(logging.ERROR))     # 输出: ERROR

print(logging.getLevelName(logging.CRITICAL))  # 输出: CRITICAL

——示例2:通过日志级别数值获取对应级别名称

import  logging

# 获取自定义日志级别名称

print(logging.getLevelName(10))  # 输出: DEBUG

16、logging.getHandlerByName(name)

返回具有指定name的处理器,如指定名称的处理器不存在则返回None。

Added in version 3.12

17、logging.makeLogRecord(attrdict)

参数

attrdict:一个字典,包含用于创建LogRecord对象的属性。字典中的键应该与LogRecord的构造函数参数相对应。

返回值

返回一个LogRecord对象,该对象包含了从attrdict中提取的属性。

创建并返回一个新的LogRecord实例,实例属性由attrdict定义。此函数适用于接受一个通过套接字传输的封存好的LogRecord属性字典,并在将其重建一个LogRecord实例。

——示例1:创建基本的LogRecord对象

import logging

# 定义属性字典

attrdict = {

    'name': 'my_logger',

    'level': logging.INFO,  # 设置日志级别

    'levelno': logging.INFO,  # 确保 levelno 也被设置

    'pathname': 'example.py',

    'filename': 'example.py',

    'module': 'example',

    'lineno': 10,

    'msg': 'This is a log message',

    'args': None,

    'exc_info': None,

    'funcName': 'my_function',

    'created': 1633072800.0,

    'msecs': 0.0,

    'relativeCreated': 1000.0,

    'thread': 12345,

    'threadName': 'MainThread',

    'process': 6789,

    'processName': 'MainProcess'

}

# 创建 LogRecord 对象

log_record = logging.makeLogRecord(attrdict)

# 输出 LogRecord 的信息

print(log_record)

# 创建日志记录器

logger = logging.getLogger('my_logger')

logger.setLevel(logging.DEBUG)

# 使用 LogRecord 对象记录日志

logger.handle(log_record)

输出:<LogRecord: my_logger, None, example.py, 10, "This is a log message">

18、logging.shutdown()

通过刷新和关闭所有处理程序来通知日志记录系统执行有序停止。此函数应当在应用退出时被调用并且此调用之后不应再使用日志记录系统。

当logging模块被导入时,它会将此函数注册为退出处理程序(参见ateexit),因此通常不需要手动执行该操作。

19、logging.setLoggerClass(class)

参数

class:一个类,应该是logging.Logger的子类。这个类将被用作后续创建的日志记录器的类。

返回值

作用

用于设置日志记录器的类。通过这个函数,开发者可以自定义日志记录器的行为,以满足特定的需求。

通知日记记录系统在实例化日志记录器时使用class类。该类应当定义__init__()使其只需要一个name参数,并且__init__()应当调用Logger.__init__()。此函数通常会在需要使用自定义日志记录器行为的应用程序实例化任何日志记录器之前被调用。在此调用之后,在其他任何时候都不要直接使用该子类来实例化日志记录器:请继续使用logging.getLogger()API来获取你的日志记录器。

——示例1:自定义日志记录器类

import logging

class MyLogger(logging.Logger):

    def my_custom_method(self):

        print("This is a custom method in MyLogger.")

# 设置自定义日志记录器类

logging.setLoggerClass(MyLogger)

# 创建一个新的日志记录器实例

my_logger = logging.getLogger('my_custom_logger')

# 使用自定义方法

my_logger.my_custom_method()  # 输出: This is a custom method in MyLogger.

——示例2:使用自定义记录器

import sys

logging.basicConfig(stream=sys.stdout)

my_logger.setLevel(logging.DEBUG)

my_logger.debug('This is a debug message')

my_logger.info('This is an info message')

输出

DEBUG:my_custom_logger:This is a debug message

INFO:my_custom_logger:This is an info message

——示例3:恢复默认日志记录器类

logging.setLoggerClass(logging.Logger)

20、logging.setLogRecordFactory(factory)

作用

用于设置自定义的日志记录工厂函数。通过这个函数开发者可以定义如何创建LogRecord对象,以便在记录日志时添加自定义的属性或行为。

参数

factory:一个可调用对象(通常是一个函数),该函数应该接受与LogRecord构造函数相同的参数,并返回一个LogRecord对象。

Added in version 3.2:此函数与getLogrecordFactory()一起提供,以便允许开发者对如何构造表示日志记录事件的LogRecord有更好的控制。

可调用对象factory具有如下签名:

factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs)

name:

日志记录器名称

level:

日志记录级别(数字)。

fn:

进行日志记录调用的文件的完整路径名。

lno:

记录调用所在文件中的行号。

msg:

日志消息。

args:

日志记录消息的参数。

exc_info:

异常元组,或 None 。

func:

调用日志记录调用的函数或方法的名称。

sinfo:

与 traceback.print_stack() 所提供的类似的栈回溯信息,显示调用的层级结构。

kwargs:

其他关键字参数。


——示例:定义自定义日志记录工厂

import logging

def custom_log_record_factory(*args, **kwargs):

    # 创建一个 LogRecord 对象

    record = logging.LogRecord(*args, **kwargs)

    # 添加自定义属性

    record.custom_attribute = 'This is a custom attribute'

    return record

# 设置自定义日志记录工厂

logging.setLogRecordFactory(custom_log_record_factory)

# 创建一个日志记录器

logger = logging.getLogger('my_logger')

# 记录日志

logger.warning('This is a warning message')

# 获取最近的日志记录

for handler in logger.handlers:

    handler.flush()

# 记录日志并访问自定义属性

record = logger.makeRecord('my_logger', logging.WARNING, 'example.py', 10, 'This is a warning message', None, None)

print(record.custom_attribute)  # 输出: This is a custom attribute

# 恢复默认日志记录工厂:

logging.setLogRecordFactory(logging.LogRecord)


九、模块级属性(2个)

1、logging.lastResort

作用

用于处理那些没有被其他处理器捕获的日志记录。这个处理器通常是一个StreamHandler,它将日志消息输出到标准错误流(stderr)。

属性说明

类型:logging.Handler的实例,通常是logging.StreamHandler

通过此属性提供的“最后处理者”。这是一个以WARNING级别写入到sys.stderr的StreamHandler,用于在没有任何日志记录配置的情况下处理日志记录事件。最终结果就是将消息打印到sys.stderr,这会替代先前形式为“no handlers coud be found for logger XYZ”的错误消息。如果出于某种原因你需要先前的行为,可将lastResort设为None。

Added in version 3.2

——示例1:查看lastResort的处理器

import logging

# 查看 lastResort 的类型

print(type(logging.lastResort))  # 通常是 <class 'logging.StreamHandler'>

# 查看 lastResort 的级别

print(logging.lastResort.level)  # 输出处理器的日志级别(如30)

——示例2:#触发 lastResort: 如果没有配置任何处理器,记录日志时将使用 lastResort。

# 创建一个日志记录器

logger = logging.getLogger('my_logger')

# 记录一条日志消息(没有配置处理器)

logger.warning('This is a warning message that will use lastResort.')

——示例3:#自定义 lastResort: 你可以自定义 lastResort 的处理器,例如更改其输出流或级别。

import sys

# 自定义 lastResort 处理器

custom_handler = logging.StreamHandler(sys.stdout)

custom_handler.setLevel(logging.ERROR)  # 只处理 ERROR 级别的日志

# 设置 lastResort 为自定义处理器

logging.lastResort = custom_handler

# 记录日志

logger = logging.getLogger('my_logger')

logger.error('This is an error message that will use the custom lastResort.')

2、logging.raiseExceptions

作用

用于控制在处理日志记录时是否抛出异常。这个属性的主要作用是决定在日志处理过程中,如果发生错误(例如,处理器在写入日志时出现问题),是否应该引发异常。

用于查看在处理过程中异常是否应当被传播。

默认值

True

如果raiseExceptions为False,则异常会被静默地忽略。这大多数情况下是日志系统所需要的——大多数用户不会关心日志系统中的错误,他们对应用程序错误更感兴趣。

——示例1:查看当前设置

import logging

# 查看当前 raiseExceptions 的值

print(logging.raiseExceptions)  # 输出: True


十、与警告模块集成

captureWarnings()函数可用来将logging和warnings模块集成。

logging.captureWarnings(capture)

作用

此函数用于打开和关闭日志系统对警告(warnings模块)的捕获。

默认值

False

如果capture是True,则warnings模块发出的警告将重定向到日志记录系统。具体来说,将使用warnings.formatwarning()格式化警告信息,将将结果字符串使用WARNING等级记录到名为'py.warnings'的记录器中。

如果capture是False,则将停止将警告重定向到日志记录系统,并且将警告重定向到其原始目标(即在captureWarnings(True)调用之前的有效目标)。

——示例1:捕获warings到日志中

import logging  

import warnings  

  

# 配置日志记录器  

logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')  

  

# 捕获警告到日志系统中  

logging.captureWarnings(True)  

  

# 发出一个警告  

warnings.warn('This is a warning message', UserWarning)  

  

# 由于捕获已开启,警告将被记录为日志消息,而不是打印到标准错误输出

输出:

2024-10-07 23:22:42,426 - WARNING - C:\Users\hyf\Desktop\Python二级\test.py:11: UserWarning: This is a warning message

  warnings.warn('This is a warning message', UserWarning)

十一、笔记问答

问:Python中有哪些常见编码可以设置

Unicode(统一码、万国码)是一种字符编码标准,旨在为世界上所有的字符提供唯一的标识符。它支持多种语言(如拉丁字母、汉字、阿拉伯字母、德文、法文、日文、韩文等)和符号,包括字母、数字、标点符号、表情符号以及其他各种字符。

GBK

中华人民共和国全国信息技术标准化技术委员会于1995年12月1日制定,1995年12月正式发布,共收录了21003个汉字

GB18030

由信息产业部和国家质量技术监督局联合发布。

GB18030-2000年版:27,533个汉字 在GBK基础上增加了CJK统一汉字扩充A的汉字

GB18030-2005年版:70,244个汉字 在GB18030-2000基础上增加了CJK统一汉字扩充B的汉字

GB18030-2022年版:87,887个(或88,115个) 增加了大量生僻汉字,覆盖广泛,是GB18030标准的最新版本

GB13000-1993年版

GB2312(淘汰)

1980年发布,支持约6700多个汉字,主要用于简体中文的书写。

但随着Unicode(特别是UTF-8)的普及,GB2312的使用逐渐减少。Unicode能够支持更多的字符和符号,并且在全球范围内得到了广泛的应用。

UTF-8(最常用)

(Unicode Transformation Format)UTF-8是UTF中最常用的编码,支持所有Unicode字符,适合处理多语言文本,是Ken Thompson于1992年创建。

UTF-8以字节为单位对Unicode进行编码,是在互联网上使用最广的一种Unicode的实现方式。

UTF-8的特点是对不同范围的字符使用不同长度的编码,顾名思义,就是使用的字节数可变。这个变化是根据Unicode编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。UTF-8编码使用的字节个数从1到4个不等,最大长度是4个字节。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

用词本编辑的时候,从文件读取的UTF-8字符被为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。

UTF-16:用两个字节表示一个字符,但有些特殊字符无法表示

UTF-32:用定长四个字节表示一个字符,优点是可以包含所有Unicode字符,检索速度快,缺点是占用内存更高,大量文本数据存储和传输时会导致带宽的浪费。

——示例:encoding='utf-8'

ASCII

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)。第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年。

仅支持128个字符,适合处理英文文本。

Big5

适用于繁体中文,支持汉字和其他字符

Big5(大五码、五大码),共收录13060个汉字。由台湾财团法人信息产业策进会五大中文套装软件所设计的中文共通内码,在1983年12月完成公告。

ISO-8859-1

适用于西欧语言,支持256个字符,常用于旧的文本文件

Windows-1252

适用于西欧语言,类似于ISO-8859-1,但包含额外的字符

Shift_JIS

适用于日文,支持汉字和其他字符

问:logging模块中,格式化字符串的三种不同格式的区别及用法

有三种不同的格式:%、{}和$。

一、%风格

这是最传统的格式化方式,类似于C语言中的printf风格。使用%符号来指定格式化字符。

——示例:

import logging

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

特点

  1. 兼容性好,适用于Python2 和Python3.
  2. 语法较为简单,但不够灵活。
  3. 不支持复杂的格式化操作。

二、{}格式

这种格式化方式使用大括号{}来指定格式化字符,属于Python的str.format()方法的风格。

——示例

import logging

formatter = logging.Formatter('{asctime} - {name} - {levelname} - {message}', style='{')

特点

更加灵活,支持更复杂的格式化操作。

只适用于Python3.2及以上版本。

可以使用str.format()的所有功能,如格式化数字、日期等。

三、$风格

这种格式化方式使用美元符号$来指定格式化字符,类似于一些其他编程语言的字符串插值方式。

——示例

import logging

formatter = logging.Formatter('${asctime} - ${name} - ${levelname} - ${message}', style='$')

特点

  1. 语法与{}风格相似,便使用$符号。
  2. 适用于Python3.2及以上版本。
  3. 适合于需要与其他语言(如JavaScript)保持一致的场景。

——示例

import logging

from datetime import datetime

# 创建一个日志记录器

logger = logging.getLogger('example_logger')

logger.setLevel(logging.DEBUG)

# 创建一个控制台处理器

console_handler = logging.StreamHandler()

# 定义日志格式,使用%格式化

formatter = logging.Formatter('%(asctime)s - %(levelname)s - Value: %(value).2f - Date: %(date)s')

# 定义日志格式,使用 {} 格式化

# formatter = logging.Formatter('{asctime} - {levelname} - Value: {value:.2f} - Date: {date}', style='{')

# 定义日志格式,使用 $ 格式化

# formatter = logging.Formatter('${asctime} - ${levelname} - Value: ${value:.2f} - Date: ${date}', style='$')

# 将格式应用到处理器

console_handler.setFormatter(formatter)

# 将处理器添加到记录器

logger.addHandler(console_handler)

# 记录一些日志

value = 3.14159

date = datetime.now().strftime('%Y-%m-%d')

logger.debug('这是一个调试信息', extra={'value': value, 'date': date})

输出:2024-10-07 14:56:22,854 - DEBUG - Value: 3.14 - Date: 2024-10-07

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值