0%

『Python基础』Logging详解

前言

Logging 是 Python 中一个记录日志的库,在项目中常常用到。


Logging

文档:https://docs.python.org/zh-cn/3/library/logging.html 基本操作已经列出,应该很少有高级操作需要再查文档了吧

这个模块为应用与库实现了灵活的事件日志系统的函数与类。其基本组成如下:

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

一个自己编写的完整的记录器构建函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import os
import sys
import time
import logging
from termcolor import colored

def create_logger(output_path, name="", dist_rank=0) -> logging.Logger:
"""Create a logger with console stream handler and file handler

Args:
output_path (str): The path of output file.
name (str, optional): The logger name. Defaults to "".
dist_rank (int, optional): The rank of the master. Defaults to 0.

Returns:
logging.Logger: return the logger
"""

# create an unique logger name with time infomation
tim = time.strftime("%y%m%d%H%M%S", time.localtime())
for i in range(1, int(1e9)):
file_path = os.path.join(output_path, f"log_{name}_{tim}{i}.log")
if not os.path.exists(file_path):
logger_name = f"log_{name}_{tim}{i}"
break
else:
raise RuntimeError("Too many log with same name in this path!")

# create logger
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
logger.propagate = False

# create formatter
fmt = '[%(asctime)s {}] (%(filename)s %(lineno)d) <%(levelname)s> : %(message)s'.format(name)
color_fmt = ' '.join((colored('[%(asctime)s {}]'.format(name), 'green'),
colored('(%(filename)s %(lineno)d)', 'blue'),
colored('<%(levelname)s>', 'yellow'),
': %(message)s'))

# create console handlers for master process to output the message into console
if dist_rank == 0:
console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.DEBUG)
console.setFormatter(logging.Formatter(fmt=color_fmt, datefmt='%Y-%m-%d %H:%M:%S'))
logger.addHandler(console)

# create file handlers to log the message
if not os.path.exists(output_path):
os.makedirs(output_path)
file = logging.FileHandler(file_path, mode='a')
file.setLevel(logging.DEBUG)
file.setFormatter(logging.Formatter(fmt=fmt, datefmt='%Y-%m-%d %H:%M:%S'))
logger.addHandler(file)

logger.info(f"The logger is create by process {os.getpid()} in {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")

return logger

记录器

1
class logging.Logger

不要实例化记录器对象,通过模块级别函数logging.getLogger(name) 获取记录器。多次使用相同的名字调用 getLogger() 会一直返回相同的 Logger 对象的引用。name 一般是句点分割的层级值,如 A.B.C 形成层次结构的记录器,根节点是 root

Logger 对象有三重任务。首先,它们向应用程序代码公开了几种方法,以便应用程序可以在运行时记录消息。其次,记录器对象根据严重性(默认过滤工具)或过滤器对象确定要处理的日志消息。第三,记录器对象将相关的日志消息传递给所有感兴趣的日志处理器。

记录器对象上使用最广泛的方法分为两类:配置和消息发送。

日志级别\(CRITICAL(50) > ERROR(40) > WARNING(30) > INFO(20) > DEBUG(10) > NOTSET(0)\) ,低于设置级别的等级的消息不会被处理。

  • 属性
    • propagate:如果这个属性为真,记录到这个记录器的事件除了会发送到此记录器的所有处理程序外,还会传递给更高级别记录器的处理器。默认为 True.
  • 方法
    • setLevel(level):给记录器设置阈值为 level 。日志等级小于 level 会被忽略。
    • **debug(msg, *args)**:在此记录器上记录 DEBUG 级别的消息。msg 是消息格式字符串,而 args 是用于字符串格式化操作合并到 msg 的参数。
    • **info(msg, *args)**:在此记录器上记录 INFO 级别的消息。
    • **warning(msg, *args)**:在此记录器上记录 WARNING 级别的消息。
    • **error(msg, *args)**:在此记录器上记录 ERROR 级别的消息。
    • **critical(msg, *args)**:在此记录器上记录 CRITICAL 级别的消息
    • **exception(msg, *args)**:在此记录器上记录 ERROR 级别的消息。
    • **log(level, msg, *args)**:在此记录器上记录 level 整数代表的级别的消息。
    • addFilter(filter), removeFilter(filter):添加删除指定的过滤器。
    • addHandler(hdlr),removeHandler(hdlr):添加删除指定的处理器

处理器

Handler 对象负责将适当的日志消息(基于日志消息的严重性)分派给处理器的指定目标。但不要直接实例化 Handler, 而是使用后面列出的常用的处理器

常用方法

  • setLevel():每个处理器中设置的级别确定该处理器将发送哪些消息
  • setFormatter():选择一个该处理器使用的 Formatter 对象。
  • addFilter()removeFilter() 分别在处理器上配置和取消配置过滤器对象。

常用处理器

  • logging.StreamHandler(stream=None):日志记录输出发送到数据流例如 sys.stdout, sys.stderr 或任何文件类对象(或者更精确地说,任何支持 write()flush() 方法的对象
    • emit(record):将记录格式化后写入流
    • flush():刷新流
    • setStream(stream):设置流,会刷新旧流再替换
    • terminator:属性,格式化终止符,默认为 \n
  • logging.FileHandler(filename, mode='a', encoding=None, delay=False, errors=None):将日志记录输出到磁盘文件中。 它从 StreamHandler 继承了输出功能。
    • mode 指定文件打开方式,delayTrue 则文件再第一次写入时才打开,errors 处理编码格式错误
    • 方法有 close()emit()
  • logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None):基于大小轮换输出日志文件
    • 只要当前日志文件长度接近 maxBytes 就会发生轮换,最多有 backupCount 个轮换文件(不包括当前)
    • doRollover():强制执行轮换
  • logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, errors=None):基于时间轮换输出文件,注意在间隔时间没有输出就不会轮换
    • when 的可能值为 \(S,M,H,D\), \(W0-W6\),\(midnight\),前四者表示轮换的时间单位秒、分、时、天,\(W0\) 表示周一,\(W6\) 表示周日以此类推(此时 \(interval\) 失效),\(midnight\) 表示午夜轮换
    • utc:判断使用 UTC 时间还是本地时间
    • atTime:表示初始化轮换时间,必须是一个 datetime.time 的实例,仅在 \(W\)\(mignight\) 起效。

格式器

格式化器对象配置日志消息的最终顺序、结构和内容。

1
logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)
  • 如果未指定 fmt ,则使用 '%(message)s'。如果未指定 datefmt,则使用 '%Y-%m-%d %H:%M:%S,uuu' 的格式,其中 uuu 表示毫秒
  • style 形参可以是 '%', '{' 或 '$' 之一,分别表示 %-formatting, str.format() 或是 string.Template 的格式化方法

格式化属性

属性名称 格式 描述
args 此属性不需要用户进行格式化。 合并到 msg 以产生 message 的包含参数的元组,或是其中的值将被用于合并的字典(当只有一个参数且其类型为字典时)。
asctime %(asctime)s 表示 LogRecord 何时被创建的供人查看时间值。 默认形式为 '2003-07-08 16:49:45,896' (逗号之后的数字为时间的毫秒部分)。
created %(created)f LogRecord 被创建的时间(即 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 消息 数字 的记录级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL) - (10, 20, 30, 40, 50).
lineno %(lineno)d 发出日志记录调用所在的源行号(如果可用)。
message %(message)s 记入日志的消息,即 msg % args 的结果。 这是在发起调用 Formatter.format() 时设置的。
module -- 模块 %(module)s 模块 (filename 的名称部分)。
msecs %(msecs)d LogRecord 被创建的时间的毫秒部分。
msg 此属性不需要用户进行格式化。 在原始日志记录调用中传入的格式字符串。 与 args 合并以产生 message,或是一个任意对象 (参见 使用任意对象作为消息)。
名称 %(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 线程名(如果可用)

过滤器

用不上,不管他

--- ♥ end ♥ ---

欢迎关注我呀~