[python logging]日志记录模块

logging 介绍

Logger从来不直接实例化,经常通过logging模块级方法(Module-Level Function)logging.getLogger(name)来获得,其中如果name不给定就用root。

名字是以点号分割的命名方式命名的(a.b.c)。对同一个名字的多个调用logging.getLogger()方法会返回同一个logger对象。这种命名方式里面,后面的loggers是前面logger的子logger,自动继承父loggers的log信息,正因为此,没有必要把一个应用的所有logger都配置一遍,只要把顶层的logger配置好了,然后子logger根据需要继承就行了。

Logger对象扮演了三重角色:

首先,它暴露给应用几个方法(logger.info(),logger.debug(),...)以便应用可以在运行时写log.

其次,Logger对象按照log信息的严重程度(信息等级)或者根据filter对象来决定如何处理log信息(默认的信息过滤功能).

最后,logger还负责把log信息传送给相关的handlers(信息处理功能).

logging框架中主要由四个部分组成:

  • Loggers: 可供程序直接调用的接口(logger.info(),logger.debug(),...)

  • Handlers: 决定将日志记录分配至正确的目的地(file,stream,emile,...)

  • Filters: 提供更细粒度的日志是否输出的判断

  • Formatters: 制定最终记录打印的格式布局(信息记录内容有哪些及格式)

loggers

loggers 就是程序可以直接调用的一个日志接口,可以直接向logger写入日志信息。logger并不是直接实例化使用的,而是通过logging.getLogger(name)来获取对象,事实上logger对象是单例模式,logging是多线程安全的,也就是无论程序中哪里需要打日志获取到的logger对象都是同一个。但是不幸的是logger并不支持多进程,这个在后面的章节再解释,并给出一些解决方案。

【注意】loggers对象是有父子关系的,当没有父logger对象时它的父对象是root,当拥有父对象时父子关系会被修正。举个例子logging.getLogger("abc.xyz")会创建两个logger对象,一个是abc父对象,一个是xyz子对象,同时abc没有父对象所以它的父对象是root。但是实际上abc是一个占位对象(虚的日志对象),可以没有handler来处理日志。但是root不是占位对象,如果某一个日志对象打日志时,它的父对象会同时收到日志,所以有些使用者发现创建了一个logger对象时会打两遍日志,就是因为他创建的logger打了一遍日志,同时root对象也打了一遍日志。

每个logger都有一个日志的级别。

当一个logger收到日志信息后先判断是否符合level,如果决定要处理就将信息传递给Handlers进行处理。

Handlers

Handlers 将logger发过来的信息进行准确地分配,送往正确的地方。举个栗子,送往控制台或者文件或者both或者其他地方(进程管道之类的)。它决定了每个日志的行为,是之后需要配置的重点区域。

每个Handler同样有一个日志级别,一个logger可以拥有多个handler也就是说logger可以根据不同的日志级别将日志传递给不同的handler。当然也可以相同的级别传递给多个handlers这就根据需求来灵活的设置了。

Filters

Filters 提供了更细粒度的判断,来决定日志是否需要打印。原则上handler获得一个日志就必定会根据级别被统一处理,但是如果handler拥有一个Filter可以对日志进行额外的处理和判断。例如Filter能够对来自特定源的日志进行拦截or修改甚至修改其日志级别(修改后再进行级别判断)。

logger和handler都可以安装filter甚至可以安装多个filter串联起来。

Formatters

Formatters 指定了最终某条记录打印的格式布局。Formatter会将传递来的信息拼接成一条具体的字符串,默认情况下Format只会将信息%(message)s直接打印出来。Format中有一些自带的LogRecord属性可以使用,如下表格:

一个Handler只能拥有一个Formatter 因此如果要实现多种格式的输出只能用多个Handler来实现。

基本使用

配置logging基本的设置,然后在控制台输出日志

import logging

# 配置全局config,可以设置不同的信息等级
# level = logging.DEBUG,INFO,WARNING,ERROR,CRITICAL严重程度一次递增。
# format : 信息记录格式
logging.basicConfig(level = logging.INFO,
                    format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

logger = logging.getLogger(__name__)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

logging.basicConfig的参数

filename:指定日志文件名;
filemode:和file函数意义相同,指定日志文件的打开模式,'w'或者'a';
format:指定输出的格式和内容,format可以输出很多有用的信息 # '%(asctime)s - %(name)s - %(message)s'
datefmt:指定时间格式,同time.strftime(), # %Y-%m-%d %H:%M:%S
level:设置日志级别,默认为logging.WARNNING;
stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略; 

format的格式说明

logging.Foamatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

属性名称

格式

说明

name

%(name)s

日志的名称

asctime

%(asctime)s

可读时间,默认格式‘2003-07-08 16:49:45,896’,逗号之后是毫秒

filename

%(filename)s

文件名,pathname的一部分

pathname

%(pathname)s

文件的全路径名称

funcName

%(funcName)s

调用日志多对应的方法名

levelname

%(levelname)s

日志的等级

levelno

%(levelno)s

数字化的日志等级

lineno

%(lineno)d

被记录日志在源码中的行数

module

%(module)s

模块名

msecs

%(msecs)d

时间中的毫秒部分

process

%(process)d

进程的ID

processName

%(processName)s

进程的名称

thread

%(thread)d

线程的ID

threadName

%(threadName)s

线程的名称

relativeCreated

%(relativeCreated)d

日志被创建的相对时间,以毫秒为单位

handler的种类

可以发现,logging有一个日志处理的主对象,其他处理方式都是通过addHandler添加进去,logging中包含的handler主要有如下几种,
StreamHandler:logging.StreamHandler;日志输出到流,可以是sys.stderr,sys.stdout或者文件
FileHandler:logging.FileHandler;日志输出到文件
BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式
RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚
TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件
SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets
DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets
SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址
SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog
NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志
MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer
HTTPHandler:logging.handlers.HTTPHandler;通过"GET"或者"POST"远程输出到HTTP服务器

消息的等级

FATAL:致命错误
CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
ERROR:发生错误时,如IO操作失败或者连接问题
WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
INFO:处理请求或者状态变化等日常事务
DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态

对应数值

级别

数值

CRITICAL

50

ERROR

40

WARNING

30

INFO

20

DEBUG

10

NOTSET

0

将日志写入到文件示例

创建一个FileHandler,并对输出消息的格式进行设置,将其添加到logger,然后将日志写入到指定的文件中

import logging

# 初始化一个logger
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)

# 创建FileHandler
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

# 记录信息
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

将日志同时输出屏幕和日志文件示例

logger中添加StreamHandler,可以将日志输出到屏幕上

import logging

# 初始化一个logger
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)

# 创建FileHandler
filehandler = logging.FileHandler("log.txt")
filehandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
filehandler.setFormatter(formatter)
logger.addHandler(filehandler)

# stream
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)

# 记录信息
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

捕获traceback示例

Python中的traceback模块被用于跟踪异常返回信息,可以在logging中记录下traceback

设置 exc_info = True

import logging

# 初始化一个logger
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)

# 创建FileHandler
filehandler = logging.FileHandler("log.txt")
filehandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
filehandler.setFormatter(formatter)
logger.addHandler(filehandler)

# stream
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)

# 记录信息
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

# 配合try except使用,捕获异常。
try:
    open("sklearn.txt","rb")
except (SystemExit, KeyboardInterrupt):
    raise
except Exception:
    logger.error("Faild to open sklearn.txt from logger.error", exc_info = True)
 
logger.info("Finish")

缓存技术加速[颜色显示]示例

"""
Modified from https://github.com/microsoft/Swin-Transformer/blob/main/logger.py
"""
 
import os
import sys
import logging
import functools
from termcolor import colored  # 命令行颜色
 
 
@functools.lru_cache()  # 使用缓存技术,加快处理速度
def create_logger(output_dir, dist_rank=0, name=""):
    # create logger
    logger = logging.getLogger(name)  # 也可以是__name__ is the module’s name 
    logger.setLevel(logging.DEBUG)
    logger.propagate = False
 
    # create formatter
    fmt = "[%(asctime)s %(name)s] (%(filename)s %(lineno)d): %(levelname)s %(message)s"
    color_fmt = (
        colored("[%(asctime)s %(name)s]", "green")
        + colored("(%(filename)s %(lineno)d)", "yellow")
        + ": %(levelname)s %(message)s"
    )
 
    # create console handlers for master process
    if dist_rank == 0:
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(
            logging.Formatter(fmt=color_fmt, datefmt="%Y-%m-%d %H:%M:%S")
        )
        logger.addHandler(console_handler)
 
    # create file handlers
    file_handler = logging.FileHandler(
        os.path.join(output_dir, f"log_rank{dist_rank}.txt"), mode="a"
    )
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(logging.Formatter(fmt=fmt, datefmt="%Y-%m-%d %H:%M:%S"))
    logger.addHandler(file_handler)
 
    return logger


特此说明:仅仅作为自己学习记录使用,防止忘记了,收藏的太多以免找不到,所有权归作者所有。

主要参考(超级详细):Python logger模块 - 浅雨凉 - 博客园 (cnblogs.com)

logging介绍部分:python logging 日志模块以及多进程日志_程序员大咖的博客-CSDN博客

你可能感兴趣的:(python,python)