Python的日志模块logging的使用

1 logging模块介绍

1、很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug(), info(), warning(), error() and critical() 5个级别,

日志共分成5个等级从低到高分别是:

  • 1)DEBUG
  • 2)INFO
  • 3)WARNING
  • 4)ERROR
  • 5)CRITICAL

对应的5中打印日志的方法分别是:debug、info、warning、error、critical,默认是WARNING,当在WARNING或之上时才被跟踪!

日志级别(从低到高)对应数值对应函数使用场景描述
DEBUG10logging.debug()最低级别,常用于调试,查看详细信息,诊断问题才会使用
INFO20logging.info()用于记录程序中正常运行的一些信息,可以理解为print的使用
WARNING30logging.warning()警告用户虽然程序还在正常运行,但可能会发生错误
ERROR40logging.error()程序出现错误,程序中的某些功能已经不能够执行
CRITICAL50logging.critical()程序出现严重错误,程序已经不能继续运行

默认级别是WARNING,表示只有WARING和比WARNING更严重的事件才会被记录到日志内,低级别的信息会被忽略。因此,默认情况下,DEBUG和INFO会被忽略,WARING、ERROR和CRITICAL会被记录(参考)。

有多种方法用来处理被跟踪的事件。最简单的方法就是把它们打印到终端控制台上。或者将它们写入一个磁盘文件内。

2、说明一下logging模块相对于print的优点(参考):

2 logging 简单示例

1、在什么都不配置和设定的情况下,logging会简单的将日志在终端打印出来,如下例所示:

import logging

logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')  # 
logging.error('This is an error')
logging.critical('This is a ciritical')

运行结果如下,因为默认日志级别是WARNING,因此只有级别大于等于该级别的日志内容被输出:

请添加图片描述

WARNING:root:Watch out!
ERROR:root:This is an error
CRITICAL:root:This is a ciritical

上面在终端中打印输出的内容包含:

  • 日志的级别(WARNING等)
  • 调用者(root)
  • 具体日志信息

当然,上面所有这些日志信息都是可以自定义的,后面会详细介绍!

2、设置日志的级别

logging.basicConfig(level=)

level可以设置的日志级别参数(参考):

  • logging.DEBUG
  • logging.INFO
  • logging.WARNING
  • logging.ERROR
  • logging.CRITICAL

如下设置:loggging.basicConfig(level=logging.DEBUG)之后就可以在终端打印输出debug和info级别的日志信息!

import logging

# 2、设置打印日志的级别
def log2():
    level = logging.DEBUG # DEBUG、INFO、WARNING、ERROR、CRITICAL
    logging.basicConfig(level=level)
    logging.debug('debug,用来打印一些调试信息,级别最低')
    logging.info('info,用来打印一些正常的操作信息')
    logging.warning('waring,用来用来打印警告信息')
    logging.error('error,一般用来打印一些错误信息')
    logging.critical('critical,用来打印一些致命的错误信息,等级最高')

if __name__ == '__main__':
    log2()

终端输出结果:

在这里插入图片描述

注意:

logging.basicConfig(level=)设置日志级别,要在输出日志之前设置才会有效!

3 logging.basicConfig()

1、logging.basicConfig()的参数除了设置日志等级,还有很多其他参数设置!

logging.basicConfig()的参数

参数名称参数作用说明
filename指定保存日志的文件名,用指定文件名创建一个FileHandler,而不是StreamHandler
filemode如果指定了文件名,则用该模式打开文件,默认是'a'模式
format指定出输出的格式和内容,默认是以冒号分割的levalname、name 和 message
datefmt使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。
style如果指定了format,则格式字符串的时候使用此风格,%{$分别对应于printf风格str.format()string.Template。默认风格为%
level指定根日志记录器级别,默认为logging.WARNING
stream指定日志的输出流,可以指定输出到sys.stderr,std.stdout 或 文件,默认输出到sys.stderr。使用指定的流初始化StramHandler,注意:stream和filename参数不兼容,如果两者同时使用,则会引发ValueError错误
handlers如果指定,这应为一个包含要加入根日志记录器的已创建处理器的可迭代对象。 任何尚未设置格式描述符的处理器将被设置为在此函数中创建的默认格式描述符。 请注意此参数与 filename 或 stream 不兼容 —— 如果两者同时存在,则会引发 ValueError。
force如果将此关键字参数指定为 true,则在执行其他参数指定的配置之前,将移除并关闭附加到根记录器的所有现有处理器。
encoding如果此关键字参数与 filename 一同被指定,则其值会在创建 FileHandler 时被使用,因而也会在打开输出文件时被使用。
errors如果此关键字参数与 filename 一同被指定,则其值会在创建 FileHandler 时被使用,因而也会在打开输出文件时被使用。 如果未指定,则会使用值 ‘backslashreplace’。 请注意如果指定为 None,它将被原样传给 open(),这意味着它将会当作传入 ‘errors’ 一样处理。

python不同版本中logging.basicConfig()参数变化:

  • python3.2 版本:增加style参数
  • python3.3 版本:增加handlers参数
  • python3.8 版本:增加force参数
  • python3.9 版本:增加encoding 和 errors参数

2、上面提到的日志的输出格式化参数format,其控制日志的输出的一些格式(参考):

对应格式格式描述
%(levelno)s日志级别
%(levelname)s日志级别名称
%(pathname)s当前程序的路径
%(filename)s当前程序的名称
%(funcName)s日志所属的当前函数
%(lineno)d日志的当前行号
%(asctime)s日志的时间
%(thread)s线程的ID
%(threadName)s线程的名称
%(process)s进程的ID
%(message)s日志的信息

例如:

format=%(levelno)s %(asctime)s %(message)s: %(linno)d

%(显示的日志内容)s:后面的s表示日志内容是字符串,显示的日志信息之间可以是空格,或者其他连接符号,只要方便自己查看即可!

上面的例子是输入日志到控制台,下面例子是输出日志信息到文件中:

import logging

def log3():
    logging.basicConfig(filename='./log.txt',
                        format='%(asctime)s-%(name)s-%(levelname)s-%(message)s-%(funcName)s:%(lineno)d',
                        level=logging.ERROR)
                        # encoding='utf-8')  # 中文乱码,在python>=3.9才有该参数
    logging.debug('debug,用来打印一些调试信息,级别最低')
    logging.info('info,用来打印一些正常的操作信息')
    logging.warning('waring,用来用来打印警告信息')
    logging.error('error,一般用来打印一些错误信息')
    logging.critical('critical,用来打印一些致命的错误信息,等级最高')
if __name__ == '__main__':
    log3()

此时在控制台并没有日志输出,如下:

在这里插入图片描述

注意:

上面测试用例中的代码,如果不使用filename参数,日志信息就会输出到控制台,如果使用filename参数,日志信息就只会输出到文件!

而是把日志输出到了文件中:

在这里插入图片描述

上述日志详情中出现了一个root字眼,这是root日志器的意思,我们也可以使用logging.getLogger()函数来自定义一个日志器

import logging

def log4():
    selflogger = logging.getLogger('THIS-LOGGING')
    logging.basicConfig(filename='./log.txt',
                     format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s-%(funcName)s',
                     level=logging.ERROR)
    selflogger.warning('waring,用来用来打印警告信息')
    selflogger.error('error,一般用来打印一些错误信息')
    selflogger.critical('critical,用来打印一些致命的错误信息,等级最高')


if __name__ == '__main__':
    log4()

输出日志内容:

在这里插入图片描述

解决中文乱码:
1、方法一:python>=3.9 在logging.basicConfig(encoding=‘utf-8’)
2、方法二:修改源码Lib/logging/init.py 中FileHandler中的encoding=None为encoding=‘utf-8’
3、方法三(参考):

4 logging组件

1、logging中除了以上的一些设置之外,logging还提供了几个组件(类)供我们实现一些特殊的功能,它们主要是(参考):

组件名称对应类名对应描述
日志器logging.Logger提供应用程序可以一直使用的接口
处理器logging.Handler将创建的日志记录发送到合适的目的输出
过滤器logging.Filter提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式器logging.Formatter决定日志记录的最终输出格式

2、这些组件共同完成日志的配置和输出:

  • Logger需要通过handler将日志信息输出到目标位置,目标位置可以是sys.stdout和文件等。
  • 不同的Handler可以将日志输出到不同的位置(不同的日志文件)。
  • Logger可以设置多个handler将同一条日志记录输出到不同的位置。
  • 每个Handler都可以设置自己的filter从而实现日志过滤,保留实际项目中需要的日志。
  • Formatter实现同一条日志以不同的格式输出到不同的地方。

简单的说就是:日志器(Logger)作为入口,通过设置处理器(Handler)的方式将日志输出,处理器再通过过滤器(Filter)格式器(Formatter)对日志进行相应的处理操作。

下面来简单介绍一下这些组件类中对应的一些常用方法

4.1 Logger类

方法方法描述
Logger.setLevel()设置日志器(Logger)将会处理的日志消息级别
Logger.addHandler()添加一个handler对象
Logger.removeHandler()移除一个handler对象
Logger.addFilter()添加一个filter对象
Logger.removeFilter()移除一个filter对象
Logger.debug()设置DEBUG级别的日志记录
Logger.info()设置INFO级别的日志记录
Logger.warning()设置WARNING级别的日志记录
Logger.error()设置ERROR级别的日志记录
Logger.critical()设置CRITICAL级别的日志记录
Logger.exception()输出堆栈追踪信息
Logger.log()设置一个自定义的level参数来创建一个日志记录

4.2 Handler类

方法方法描述
Handler.setLevel()设置处理器将会处理的日志消息的最低严重级别
Handler.setFormatter()为处理器设置一个格式对象
Handler.addFilter()为处理器添加一个过滤器对象
Handler.removeFilter()为处理器删除一个过滤器对象
logging.StramHandler()将日志消息发送到输出Stream,如std.out,std.err
logging.FilterHandler()将日志消息发送到磁盘文件,默认情况文件大小会无线增长
RotationFileHandler()将日志消息发送到磁盘文件,支持日志文件按大小切割
TimeRotatingFileHandler()将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handers.HTTPHandler()将日志消息通过GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler()将日志消息发送email地址

4.3 Filter类

logging.Filter是一个过滤类,那具体过滤什么呢?

Hanler和Logger可以使用Filter类来设置日志等级level更细粒度、更复杂的相关过滤功能。

该类的定义:

class logging.Filter(name='')
    filter(record)

过滤举例:

例如用 ‘A.B’ 初始化的 Filter,那么其允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 等日志记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不满足过滤的条件。

如果用空字符串来对Filter初始化,所有日志记录的事件都将不会被过滤。

4.4 Formatter类

Formater类主要负责日志的格式化输出的。

可通过logging.basicConfig或logging.Formatter函数来配置日志输出内容。

如:formatter = logging.Formatter(fmt=None, datefmt=None)。

如果不指明 fmt,将默认使用 ‘%(message)s’ ,如果不指明 datefmt,将默认使用 ISO8601 日期格式。

4.5 logging模块输出日志到文件和控制台

说了这么多,我们先看两个简单的例子:

1、输出日志到文件和控制台

import logging
from logging.handlers import RotatingFileHandler


def log5():
    # 创建日志的记录等级
    logging.basicConfig(level=logging.DEBUG)
    # 创建日志记录器,指明日志保存的路径,每个日志文件的最大值,保存的日志文件个数上限
    log_handle = RotatingFileHandler('./log.txt', maxBytes=1024*1024, backupCount=5)
    # 创建日志记录的格式
    formatter = logging.Formatter("format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s-%(funcName)s',")
    # 为创建的日志记录器设置日志记录格式
    log_handle.setFormatter(formatter)
    # 为全局的日志工具对象添加日志记录器
    logging.getLogger().addHandler(log_handle)
    logging.warning('用来用来打印警告信息')
    logging.error('一般用来打印一些错误信息')
    logging.critical('用来打印一些致命的错误信息,等级最高')

if __name__ == '__main__':
    log5()

在终端输出日志:

在这里插入图片描述

在日志文件中输出日志:
在这里插入图片描述

2、输出日志到文件 和 输出日志到控制台,设置不同的日志等级

输出日志的流程:

在这里插入图片描述

import logging


def log6():
    # 1、设置日志器的名字,默认是root
    logger = logging.getLogger('updateSecurity')
    # 设置全局的日志级别
    logger.setLevel('DEBUG')           #设置了这个才会把debug以上的输出到控制台
    
    # 输出到文件 和 输出到控制台的格式可以定义不同
    selfdef_fmt = '%(asctime)s - %(funcName)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(selfdef_fmt)
    
    # 2、设置输出日志到文件
    # encoding='utf-8' 输出到文件中的中文日志就不会乱码了
    file_handler = logging.FileHandler(filename='./log.txt', encoding='utf-8', mode='a') # stdout to file
    # 此时输出到文件单独设置了日志级别为ERROR,此时输出到文件中的日志级别为ERROR
    file_handler.setLevel('ERROR')             
    file_handler.setFormatter(formatter)
    # 将file_handler加入到logger内
    logger.addHandler(file_handler)    # 添加handler
    
    # 3、设置输出日志到控制台
    control_handler = logging.StreamHandler()    # stdout to console
    control_handler.setLevel('WARNING')             # 设置INFO级别
    control_handler.setFormatter(formatter)
    # 将control_handler加入到logger内
    logger.addHandler(control_handler)   # 把控制台日志对象添加到logger中
	
	# 4、程序调用logger
    logger.info('info,一般的信息输出')
    logger.warning('waring,用来用来打印警告信息')
    logger.error('error,一般用来打印一些错误信息')
    logger.critical('critical,用来打印一些致命的错误信息,等级最高')


if __name__ == '__main__':
    log6()
    

在这里插入图片描述

在日志文件中输出日志:
在这里插入图片描述

5 把日志的配置信息写到配置文件中

1 conf形式的配置文件

我们知道在logging.basicConfig()中,是进行一些日志的配置的,如果每次都去改动代码,那将变得十分的麻烦,特别是在程序发布或者上线的时候。所以我们可以使用一个什么好的方法来规避这个问题呢?有的,那就是配置文件。

我们在文件loguser.conf中写入相关的信息,内容如下:

[loggers]
keys=root,fileLogger,rotatingFileLogger

[handlers]
keys=consoleHandler,fileHandler,rotatingFileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=INFO
handlers=consoleHandler

[logger_fileLogger]
level=INFO
handlers=fileHandler
qualname=fileLogger
propagate=0

[logger_rotatingFileLogger]
level=INFO
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=("logs/fileHandler_test.log", "a")

[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=("logs/rotatingFileHandler.log", "a", 10*1024*1024, 50)

[formatter_simpleFormatter]
format=%(asctime)s - %(module)s - %(levelname)s -%(thread)d : %(message)s
datefmt=%Y-%m-%d %H:%M:%S

使用配置文件中的代码如下:

import os
import logging
from logging import config

def log7():
    if os.path.exists('./loguser.conf'):
        with open('./loguser.conf', 'r', encoding='utf-8') as f:
            config.fileConfig(f)
    
    # 创建一个日志器
     #创建一个日志器logger
    logger = logging.getLogger(name="fileLogger")
    rotating_logger = logging.getLogger(name="rotatingFileLogger")

    logger.debug('debug')
    logger.info('info')
    logger.warning('warn')
    logger.error('error')
    logger.critical('critical')

    rotating_logger.debug('debug')
    rotating_logger.info('info')
    rotating_logger.warning('warn')
    rotating_logger.error('error')
    rotating_logger.critical('critical')

if __name__ == '__main__':
    log7()

输出到控制台日志:
在这里插入图片描述

输出到文件中日志:

在这里插入图片描述

在这里插入图片描述

5.1 yaml形式配置文件

1、假设我们的loguser.yaml配置文件的内容如下:

version: 1
disable_existing_loggers: False
# formatters配置了日志输出时的样式
# formatters定义了一组formatID,有不同的格式;
formatters:
  brief:
      format: "%(asctime)s - %(message)s"
  simple:
      format: "%(asctime)s - [%(name)s] - [%(levelname)s] :%(levelno)s: %(message)s"
      datefmt: '%F %T'
# handlers配置了需要处理的日志信息,logging模块的handler只有streamhandler和filehandler
handlers:
  console:
      class : logging.StreamHandler
      formatter: brief
      level   : DEBUG
      stream  : ext://sys.stdout
  info_file_handler:
      class : logging.FileHandler
      formatter: simple
      level: ERROR
      filename: ./logs/debug_test.log
  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: ./logs/errors.log
    maxBytes: 10485760 # 10MB #1024*1024*10
    backupCount: 50
    encoding: utf8

loggers:
#fileLogger, 就是在代码中通过logger = logging.getLogger("fileLogger")来获得该类型的logger
  my_testyaml:
      level: DEBUG
      handlers: [console, info_file_handler,error_file_handler]
# root为默认情况下的输出配置, 当logging.getLogger("fileLoggername")里面的fileLoggername没有传值的时候,
# 就是用的这个默认的root,如logging.getLogger(__name__)或logging.getLogger()
root:
    level: DEBUG
    handlers: [console]

3、使用loguser.yaml配置文件代码如下:

import logging
import os
from logging import config
import yaml


def log8():
    if os.path.exists('loguser.yaml'):
        with open('./loguser.yaml', 'r', encoding='utf-8') as f:
            yaml_config = yaml.load(stream=f, Loader=yaml.FullLoader)
            config.dictConfig(config=yaml_config)
    else:
        logging.basicConfig(level=logging.INFO)

    root = logging.getLogger()
    # 子记录器的名字与配置文件中loggers字段内的保持一致
    # loggers:
    #   my_testyaml:
    #       level: DEBUG
    #       handlers: [console, info_file_handler,error_file_handler]
    my_testyaml = logging.getLogger("my_testyaml")
    print("rootlogger:", root.handlers)
    print("selflogger", my_testyaml.handlers)
    # 判断子记录器与根记录器的handler是否相同
    print(root.handlers[0] == my_testyaml.handlers[0])

    my_testyaml.info("INFO")
    my_testyaml.error('ERROR')
    my_testyaml.debug("rootDEBUG")

    root.info("INFO")
    root.error('ERROR')
    root.debug("rootDEBUG")

if __name__ == '__main__':
    log8()

输出到控制台日志:

在这里插入图片描述

输出到文件中日志:

在这里插入图片描述

在这里插入图片描述

参考:https://docs.python.org/zh-cn/3/library/logging.html # 官方教程
参考:https://www.cnblogs.com/alex3714/articles/5161349.html
参考:https://cuiqingcai.com/6080.html
参考:https://www.jianshu.com/p/feb86c06c4f4
参考:https://blog.csdn.net/huilan_same/article/details/77869225
参考:https://www.jianshu.com/p/7776e9a50827
参考:https://www.liujiangblog.com/course/python/71
参考:https://zhuanlan.zhihu.com/p/166671955
参考:https://zhuanlan.zhihu.com/p/360306588
参考:http://c.biancheng.net/view/5734.html
参考


参考日志实例:

#Reference: https://github.com/ultralytics/yolov5/blob/master/val.py 中记录日志

https://github.com/JDAI-CV/FaceX-Zoo/blob/main/face_sdk/logs/sdk.log
https://github.com/JDAI-CV/FaceX-Zoo/blob/main/face_sdk/api_usage/face_detect.py # 从配置文件中读取日志设置!

参考:https://zhuanlan.zhihu.com/p/166671955
参考:https://www.liujiangblog.com/course/python/71
参考:https://zhuanlan.zhihu.com/p/360306588
参考:https://cuiqingcai.com/6080.html
参考:https://docs.python.org/zh-cn/3/library/logging.html

  • 32
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值