python logging模块详解

logging库采用模块化方法,并提供了几类组件:记录器,处理程序,过滤器和格式化程序。

  • 记录器(Logger):提供应用程序代码直接使用的接口。

  • 处理器(Handler):将日志记录(由记录器创建)发送到适当的目的地。

  • 筛选器(Filter):提供了更细粒度的功能,用于确定要输出的日志记录。

  • 格式器(Formatter):程序在最终输出日志记录的内容格式。

logging的工作流程:以记录器Logger为对象,设置合适的处理器Handler,辅助以筛选器Filter、格式器Formatter,设置日志级别以及常用的方法,最终输出理想的日志记录给到指定目标

  • 一个Logger可以包含多个Handler;
  • 每个Handler可以设置自己的Filter和Formatter;
    在这里插入图片描述

一、基本使用

import logging

# 创建日志器对象
logger = logging.getLogger(__name__)

# 设置logger可输出日志级别范围
logger.setLevel(logging.DEBUG)

# 添加控制台handler,用于输出日志到控制台
console_handler = logging.StreamHandler()
# 添加日志文件handler,用于输出日志到文件中
file_handler = logging.FileHandler(filename='log.log', encoding='UTF-8')

# 将handler添加到日志器中
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 设置格式并赋予handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 输出不同级别日志
logger.debug("============【开始测试】====================")
logger.info("============【开始测试】====================")
logger.warning("============【开始测试】====================")
logger.critical("============【开始测试】====================")
logger.error("============【开始测试】====================")

输出结果:
在这里插入图片描述
上面是一个完整的实例,接下来详细讲logging模块

二、日志器Logger

logging模块中,要输出日志,日志器是必不可少的
创建日志器:

logger = logging.getLogger()

日志器名称:
1、若没有指定日志器名称,则默认为root

print(logging.getLogger())

输出结果:<RootLogger root (WARNING)>

2、当有多个具有相同名称的日志器,调用任一日志器时将返回对同一记录器对象的引用
注意:这种情况下日志器间的处理器handler是相互叠加,由于logging每次输出日志记录数量=handler数量,我们会看到logger.warning("输出一条日志记录")的实际结果会打印多条日志记录

import logging

logger = logging.getLogger()  # 日志器名称默认为root
logger1 = logging.getLogger()  # 日志器名称默认为root

# 设置两个处理器handler
console_handler = logging.StreamHandler()
console_handler1 = logging.StreamHandler()

# 给两个相同名称的logger添加上处理器
logger.addHandler(console_handler)
logger1.addHandler(console_handler1)

# 设置一下格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
console_handler1.setFormatter(formatter)

# 输出日志记录
logger1.warning("输出一条日志记录")

输出结果:
在这里插入图片描述
从这里看出来,logger1在代码中,我们只给他添加了一个handler,但是由于和logger名称相同,调用任一一个logger,他们的处理器相加为2了,且因日志记录数量=处理器数量,所以这里代码只记录一条,但实际结果是两条日志记录,因此不太建议这么用

3、通常建议使用__name__来命名日志器,或者自定义的名称,这样日志器名称不相同,则不会产生handler叠加的现象

import logging

logger = logging.getLogger(__name__)  # 日志器名称为包名
logger1 = logging.getLogger("DIY")  # 日志器名称为DIY

# 设置两个处理器handler
console_handler = logging.StreamHandler()
console_handler1 = logging.StreamHandler()

# 给两个不同名称的logger添加上处理器
logger.addHandler(console_handler)
logger1.addHandler(console_handler1)

# 设置一下格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
console_handler1.setFormatter(formatter)

# 输出日志记录
logger1.warning("输出一条日志记录")

输出结果:
在这里插入图片描述
这里两个logger名称不相同,因此没有handler相加的现象,所以这里正常输出一条日志记录

日志器数量
从上面的例子也可以看出,logger日志器的数量可以是多数的,理论上应该是无限的

logger1 = logging.getLogger(__name__)
logger2 = logging.getLogger(__name__)
logger3 = logging.getLogger(__name__)
logger4 = logging.getLogger(__name__)
logger5 = logging.getLogger(__name__)

三、处理器Handler

处理器负责将日志消息发送到不同目标,例如:控制台,日志文件,电子邮件,等目标
logging模块提供很多个类型的处理器
在这里插入图片描述

StreamHandler

日志记录发送到控制台中

logging.StreamHandler(stream=None

参数:
stream:默认为None,日志输出到sys.stderr;指定stream的话中,则日志输出到指定stream

FileHandler

日志记录发送到磁盘文件中

logging.FileHandler(filename, mode='a', encoding=None, delay=False)

参数:
filename:只填写文件名称,则默认新增文件到当前工作目录;填写工作路径+文件名称,则新增到对应工作目录
mode:默认a模式,可自定义
encoding:默认为None,可自定义
delay:默认为False,为True时,将文件打开延迟到第一次调用时进行emit()

NullHandler

不做任何事情的处理器,防止sys.stderr在没有日志记录配置的情况下将库的已记录事件输出

logging.NullHandler()

WatchedFileHandler

该处理器旨在在Unix / Linux下使用,它监视文件以查看自上一次发出以来是否已更改。(如果文件的设备或索引节点已更改,则认为该文件已更改。)如果文件已更改,则关闭旧文件流,并打开该文件以获取新的流,不适合在Windows下使用

logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)

RotatingFileHandler

日志记录到文件中,且支持指定日志文件大小,备份文件数量

logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)

参数:
maxBytes:日志文件大小,单位为字节
backupCount:备份文件数量

注意:当maxBytes或backupCount中的任何一个为零,则永远不会发生过渡

例子:使用backupCount 5和的基本文件名app.log,你会得到app.log, app.log.1,app.log.2,达到app.log.5。写入的文件始终为app.log。当这个文件被填满时,它被关闭并重新命名为app.log.1,如果文件app.log.1, app.log.2等存在,那么它们被重命名为app.log.2, app.log.3分别等。

实例:

import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger('logger')
logger.setLevel(logging.DEBUG)
rotating_handler = logging.handlers.RotatingFileHandler('rotating_log.log', encoding='UTF-8', maxBytes=1024, backupCount=2)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
rotating_handler.setFormatter(formatter)
logger.addHandler(rotating_handler)

# 输出日志记录
logger.debug("============【开始测试】====================")
logger.info("============【开始测试】====================")
logger.warning("============【开始测试】====================")
logger.error("============【开始测试】====================")
logger.critical("============【开始测试】====================")

输出结果:
在这里插入图片描述

TimedRotatingFileHandler

日志记录到文件中,支持按时间间隔来更新日志

logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)

参数:
when:不同值对应不同时间类型,以字符串填入,不区分大小写
在这里插入图片描述
基于工作日的轮换时,将“ W0”指定为星期一,将“ W1”指定为星期二,依此类推,直到“ W6”指定为星期日。在这种情况下,不使用为interval传递的值

关于过渡时间:首次(在创建处理程序时)计算下一个过渡时间时,将使用现有日志文件的最后修改时间或当前时间来计算下一个轮换发生的时间。

interval:时间长度,与when结合使用

backupCount:如果backupCount不为零,则最多将保留backupCount数的文件,并且如果发生翻转时将创建更多文件,则最早的文件将被删除。删除逻辑使用间隔来确定要删除的文件,因此更改间隔可能会留下旧文件。

utc:默认为False,使用本地时间;为True时,使用UTC时间

actime:默认为None,若非None时,必须是一个datetime.time实例,该实例指定发生翻转的一天中的时间,对于将翻转设置为“在午夜”或“在特定工作日”发生的情况。请注意,在这些情况下,atTime值可有效地用于计算初始 翻转,随后的翻转将通过正常间隔计算来计算。
在这里插入图片描述
实例:

import logging
from logging.handlers import RotatingFileHandler
from datetime import time

logger = logging.getLogger('logger')
logger.setLevel(logging.DEBUG)
time_rotating_handler1 = logging.handlers.TimedRotatingFileHandler('time_rotating_log.log', when='M', interval=1.5, backupCount=2, encoding='UTF-8', delay=False, utc=False, atTime=time)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
time_rotating_handler1.setFormatter(formatter)
logger.addHandler(time_rotating_handler1)

# 输出日志记录
logger.debug("============【开始测试】====================")
logger.info("============【开始测试】====================")
logger.warning("============【开始测试】====================")
logger.error("============【开始测试】====================")
logger.critical("============【开始测试】====================")

结果:
在这里插入图片描述

其他handler

其他还有SocketHandler、DatagramHandler、SysLogHandler、NTEventLogHandler、SMTPHandler、MemoryHandler、HttpHandler、QueueHandler、QueueListener等,有兴趣的童鞋可以看官网:https://docs.python.org/3.8/library/logging.handlers.html#logging.handlers

四、过滤器Filter

Filters可以由级别使用,Handlers并且可以用于Loggers比级别所提供的更复杂的过滤。基本过滤器类仅允许记录器层次结构中特定点以下的事件。例如,使用“ AB”初始化的过滤器将允许记录器“ A.B”,“ ABC”,“ ABCD”,“ ABD”等记录的事件,但不允许“ A.BB”,“ BAB”等记录。使用空字符串,将传递所有事件。

官网给出的实例:

import logging
from random import choice

class ContextFilter(logging.Filter):
    """
    This is a filter which injects contextual information into the log.

    Rather than use actual contextual information, we just use random
    data in this demo.
    """

    USERS = ['jim', 'fred', 'sheila']
    IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']

    def filter(self, record):

        record.ip = choice(ContextFilter.IPS)
        record.user = choice(ContextFilter.USERS)
        return True

if __name__ == '__main__':
    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s')
    a1 = logging.getLogger('a.b.c')
    a2 = logging.getLogger('d.e.f')

    f = ContextFilter()
    a1.addFilter(f)
    a2.addFilter(f)
    a1.debug('A debug message')
    a1.info('An info message with %s', 'some parameters')
    for x in range(10):
        lvl = choice(levels)
        lvlname = logging.getLevelName(lvl)
        a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')

输出结果:

2010-09-06 22:38:15,292 a.b.c DEBUG    IP: 123.231.231.123 User: fred     A debug message
2010-09-06 22:38:15,300 a.b.c INFO     IP: 192.168.0.1     User: sheila   An info message with some parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1       User: sheila   A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR    IP: 127.0.0.1       User: jim      A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG    IP: 127.0.0.1       User: sheila   A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR    IP: 123.231.231.123 User: fred     A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1     User: jim      A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1       User: sheila   A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG    IP: 192.168.0.1     User: jim      A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f ERROR    IP: 127.0.0.1       User: sheila   A message at ERROR level with 2 parameters
2010-09-06 22:38:15,301 d.e.f DEBUG    IP: 123.231.231.123 User: fred     A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f INFO     IP: 123.231.231.123 User: fred     A message at INFO level with 2 parameters

实例2:

#!/bin/python
# -*- encoding:utf-8 -*-

import sys
import logging


class ContextFilter(logging.Filter):
    """
    这是一个控制日志记录的过滤器。
    """
    def filter(self, record):
        try:
            filter_key = record.TASK
        except AttributeError:
            return False

        if filter_key == "logToConsole":
            return True
        else:
            return False


if __name__ == '__main__':
    # 创建日志对象
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    # 创建日志处理器,记录日志到文件
    log_path = "./log.log"
    file_handler = logging.FileHandler(log_path)
    file_handler.setLevel(logging.INFO)
    file_fmt = "%(asctime)-15s %(name)s %(levelname)s [%(filename)s %(lineno)d] %(message)s"
    file_formatter = logging.Formatter(file_fmt)
    file_handler.setFormatter(file_formatter)
    logger.addHandler(file_handler)

    # 添加日志处理器,输出日志到控制台
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.WARN)

    console_fmt = '%(asctime)s %(name)s [%(TASK)s] %(message)s'
    console_formatter = logging.Formatter(console_fmt)
    console_handler.setFormatter(console_formatter)

    console_filter = ContextFilter()
    console_handler.addFilter(console_filter)

    logger.addHandler(console_handler)

    filter_dict = {'TASK': 'logToConsole'}

    # 记录日志
    logger.debug('debug message')
    logger.info('info message')
    logger.warning('warn message')
    logger.error('error message1', extra=filter_dict)
    logger.error('error message2')

实例3:

import logging.config


class MyFilter(logging.Filter):
    def __init__(self, param=None):
        super().__init__()
        self.param = param

    def filter(self, record):
        if self.param is None:
            allow = True
        else:
            allow = self.param not in record.msg
        if allow:
            record.msg = 'changed: ' + record.msg
        return allow


LOGGING = {
    'version': 1,
    'filters': {
        'myfilter': {
            '()': MyFilter,
            'param': 'noshow',
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'filters': ['myfilter']
        }
    },
    'root': {
        'level': 'DEBUG',
        'handlers': ['console']
    },
}

if __name__ == '__main__':
    logging.config.dictConfig(LOGGING)
    logging.debug('hello')
    logging.debug('hello - noshow')

五、格式器Formatter

格式器可以初始化日志记录的内容格式,结合LogRecord对象提供的属性,可以设置不同的日志格式

logging.Formatter(fmt=None, datefmt=None, style='%')

参数:
fmt:日志格式参数,默认为None,如果不特别指定fmt,则使用’%(message)s’格式
datafmt:时间格式参数,默认为None,如果不特别指定datafmt,则使用formatTime()文档中描述的格式。
style:风格参数,默认为’%’,也支持’$’,’{'格式

LogRecord的属性
以下例子均为’%'格式

实例:

import logging

# 创建日志器对象
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 设置handler
console_handler = logging.StreamHandler()

# 添加处理器
logger.addHandler(console_handler)

# 设置格式
formatter = logging.Formatter('%(asctime)s  %(created)f  %(name)s  %(levelname)s  %(filename)s  %(funcName)s  '
                              '%(levelno)s  %(lineno)d  %(module)s  %(msecs)d  %(pathname)s  %(process)d  '
                              '%(processName)s  %(relativeCreated)d  %(thread)d  %(threadName)s  %(message)s')
console_handler.setFormatter(formatter)
# formatter = logging.Formatter('${name} - ${levelname} - ${message}', style='$')
# formatter = logging.Formatter('{name} - {levelname} - {message}', style='{')


if __name__ == '__main__':
    # 输出日志记录
    logger.debug("============【开始测试】====================")
    logger.info("============【开始测试】====================")
    logger.warning("============【开始测试】====================")
    logger.error("============【开始测试】====================")
    logger.critical("============【开始测试】====================")

输出结果:

2020-05-21 17:59:00,306  1590055140.306499  __main__  DEBUG  test_logger_formatter.py  <module>  10  23  test_logger_formatter  306  E:/PyProject/Test_Pratice/test_logger_formatter.py  21872  MainProcess  0  22460  MainThread  ============【开始测试】====================
2020-05-21 17:59:00,306  1590055140.306499  __main__  INFO  test_logger_formatter.py  <module>  20  24  test_logger_formatter  306  E:/PyProject/Test_Pratice/test_logger_formatter.py  21872  MainProcess  0  22460  MainThread  ============【开始测试】====================
2020-05-21 17:59:00,306  1590055140.306499  __main__  WARNING  test_logger_formatter.py  <module>  30  25  test_logger_formatter  306  E:/PyProject/Test_Pratice/test_logger_formatter.py  21872  MainProcess  0  22460  MainThread  ============【开始测试】====================
2020-05-21 17:59:00,306  1590055140.306499  __main__  ERROR  test_logger_formatter.py  <module>  40  26  test_logger_formatter  306  E:/PyProject/Test_Pratice/test_logger_formatter.py  21872  MainProcess  0  22460  MainThread  ============【开始测试】====================
2020-05-21 17:59:00,306  1590055140.306994  __main__  CRITICAL  test_logger_formatter.py  <module>  50  27  test_logger_formatter  306  E:/PyProject/Test_Pratice/test_logger_formatter.py  21872  MainProcess  0  22460  MainThread  ============【开始测试】====================

注意:每个处理器只能有一个formatter,当给处理器多次设置formatter时,以最后一个为准

六、设置日志级别

logging库设定6个日志记录级别,我们可以通过设置日志级别,来决定哪些级别范围的日志记录可以输出
在这里插入图片描述

使用setLevel() 方法设置日志级别

注意:若没有设置日志级别,日志器的日志级别默认为WARNING

给日志器设置日志级别

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
logger.addHandler(console_handler)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

if __name__ == '__main__':
    # 输出日志记录
    logger.debug("============【开始测试】====================")
    logger.info("============【开始测试】====================")
    logger.warning("============【开始测试】====================")
    logger.error("============【开始测试】====================")
    logger.critical("============【开始测试】====================")

输出结果:DEBUG级别的日志记录没有输出,因为日志级别低于设定的INFO级别

2020-05-21 18:24:40,851 - __main__ - INFO - ====================【开始测试】====================
2020-05-21 18:24:40,851 - __main__ - WARNING - ====================【开始测试】====================
2020-05-21 18:24:40,851 - __main__ - ERROR - ====================【开始测试】====================
2020-05-21 18:24:40,851 - __main__ - CRITICAL - ====================【开始测试】====================

给处理器设置日志级别

import logging

logger = logging.getLogger(__name__)
# logger.setLevel(logging.INFO)

# 设置两个处理器
console_handler = logging.StreamHandler()
console_handler1 = logging.StreamHandler()

# 设置处理器日志级别分别INFO,WARNING
console_handler.setLevel(logging.INFO)
console_handler1.setLevel(logging.WARNING)

logger.addHandler(console_handler)
logger.addHandler(console_handler1)

formatter = logging.Formatter('我是console_handler:%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter1 = logging.Formatter('我是console_handler1:%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
console_handler1.setFormatter(formatter1)


if __name__ == '__main__':
    # 输出日志记录
    logger.debug("============【开始测试】====================")
    logger.info("============【开始测试】====================")
    logger.warning("============【开始测试】====================")
    logger.error("============【开始测试】====================")
    logger.critical("============【开始测试】====================")

输出结果:console_handler设置级别为INFO,因此输出INFO及以上日志级别记录,console_handler1设置级别为WARNING,因此没有低于WARNING的日志级别记录

我是console_handler:2020-05-21 18:32:18,548 - __main__ - INFO - ====================【开始测试】====================
我是console_handler:2020-05-21 18:32:18,548 - __main__ - WARNING - ====================【开始测试】====================
我是console_handler1:2020-05-21 18:32:18,548 - __main__ - WARNING - ====================【开始测试】====================
我是console_handler:2020-05-21 18:32:18,548 - __main__ - ERROR - ====================【开始测试】====================
我是console_handler1:2020-05-21 18:32:18,548 - __main__ - ERROR - ====================【开始测试】====================
我是console_handler:2020-05-21 18:32:18,548 - __main__ - CRITICAL - ====================【开始测试】====================
我是console_handler1:2020-05-21 18:32:18,548 - __main__ - CRITICAL - ====================【开始测试】====================

注意:
1、日志器的级别作用于属于日志器的所有处理器
2、若同时设置了日志器、处理器的日志级别,则以最高的那个级别为标准来输出
3、若设置日志级别为NOTEST时,则遍历其祖先记录器链,直到找到非NOTSET级别的祖先或到达根为止(根记录器是使用level创建的WARNING)。
如果发现某个祖先的级别不是NOTSET,那么该祖先的级别将被视为祖先搜索开始的记录器的有效级别,并用于确定如何处理日志事件。
如果到达根目录,并且其级别为NOTSET,则将处理所有消息。否则,将使用根的级别作为有效级别。

七、输出日志记录

logging模块提供5个日志级别的输出方法
两种方式调用,分别是logging直接调用,或者日志器调用,我们常用的都是创建日志器对象来调用

debug:info、warning、erro、critical方法,参数都一致

debug(msg, *args, **kwargs)

参数:
msg:日志消息格式字符串
args:args是被合并到参数 MSG使用字符串格式化操作,当未提供args时,不会对msg执行%格式化操作。
kwargs:kwargs中检查了三个关键字参数: exc_info,stack_info和extra。

  • exc_info:是否将异常信息添加到日志消息的参数,默认为False,若不为False,则将异常信息添加到日志消息中。

  • stack_info:是否将堆栈信息添加到日志消息的参数,默认为False,若不为False,则将堆栈信息添加到日志消息中

  • extra:可用于传递字典,该字典用于使用LogRecord 用户定义的属性填充为日志记录事件创建的__dict__ 。然后可以根据需要使用这些自定义属性。

exc_info实例:没有错误时,会在下一行输出NoneType: None

logger.debug("====================【开始测试】====================", exc_info=True)

输出结果:

2020-05-21 19:37:03,421 - __main__ - DEBUG - ====================【开始测试】====================
NoneType: None

当有错误时:会在日志记录的下一行,输出Traceback信息

try:
    open("notreal.txt", "rb")
except Exception:
    logger.debug("Faild to open notreal.txt from logger.debug", exc_info=True)

输出结果:

2020-05-21 19:42:16,673 - __main__ - DEBUG - Faild to open notreal.txt from logger.debug
Traceback (most recent call last):
  File "E:/PyProject/Test_Pratice/test_logger_formatter.py", line 40, in <module>
    open("notreal.txt", "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'notreal.txt'

stack_info实例:在日志记录的下一行,输出堆栈信息

logger.debug("====================【开始测试】====================", stack_info=True)

输出结果:

2020-05-21 19:45:51,822 - __main__ - DEBUG - ====================【开始测试】====================
Stack (most recent call last):
  File "E:/PyProject/Test_Pratice/test_logger_formatter.py", line 34, in <module>
    logger.debug("====================【开始测试】====================", stack_info=True)

extra实例:

FORMAT = '%(asctime)-15s %(clientip)s %(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)

输出结果

2020-05-21 19:51:11,313 192.168.0.1 fbloggs  Protocol problem: connection reset

八、日志记录配置

logging模块提供一个配置方法,以字典为容器,设置日志器、处理器、格式器等参数,使用logging.config.dictConfig(配置字典)方法生成对应的元器件

实例:

import logging.handlers
import logging.config

config = {
    'version': 1,  # 必填项,值只能为1
    'disable_existing_loggers': True,  # 选填,默认为True,将以向后兼容的方式启用旧行为,此行为是禁用任何现有的非根日志记录器,除非它们或它们的祖先在日志配置中显式命名。如果指定为False,则在进行此调用时存在的记录器将保持启用状态
    'incremental': False,  # 选填,默认为False,作用,为True时,logging完全忽略任何formatters和filters,仅处理handlers的level

    'formatters':  # 格式器配置专用key,在这里配置formatter,可配置复数formatter
        {
            'myformatter1': {
                'class': 'logging.Formatter',  # 必填,格式器对应的类
                'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',  # fmt格式
                'datefmt': '%Y-%m-%d %H:%M:%S'  # 日期时间格式
            },
            # 'myformatter2': {
            #     '()': 'my_diy_formatter',  # 将class改为(),代表不使用logging的类,使用我们重新定义的类
            #     'format': '%(asctime)s - %(levelname)s - %(message)s',  # fmt格式
            #     'datefmt': '%Y-%m-%d %H:%M:%S'  # 日期时间格式
            # }
        },

    'handlers':  # 处理器配置专用key,在这里配置handler,可配置复数handler
        {
            'console_handler': {
                'class': 'logging.StreamHandler',  # 必填,处理器对应的类
                'level': logging.DEBUG,  # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO
                'formatter': 'myformatter1',  # 选填,这里要填写formatters字典中的键
            },
            'file_handler': {
                'class': 'logging.handlers.RotatingFileHandler',  # 必填,处理器对应的类
                'level': logging.INFO,  # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO
                'formatter': 'myformatter1',  # 选填,这里要填写formatters字典中的键
                'filename': './mylog.log',  # filehandler特有参数,文件名
                'maxBytes': 1024*1024,  # 文件大小
                'backupCount': 3,  # 备份数量
                'encoding': 'UTF-8',  # 编码格式
            }
        },

    'loggers':  # 记录器配置专用key,在这里配置logger,可配置复数logger
        {
            'logger1': {
                'handlers': ['console_handler', 'file_handler'],  # 列表形式,元素填handlers字典中的handler
                'level': logging.DEBUG,  # 选填,记录器的日志级别,不填则默认Warning级别
                'propagate': False,  # 选填,为False时,禁止将日志消息传递给父级记录器
            }
        },

    'root':  # 根记录器专用key
        {
            'handlers': ['console_handler', 'file_handler'],  # 列表形式,元素填handlers字典中的handler
            'level': logging.DEBUG,  # 选填,记录器的日志级别,不填则默认Warning级别
        }
}
# 根据配置字典,配置对应元器件
logging.config.dictConfig(config)

if __name__ == '__main__':
    logger = logging.getLogger('logger1')  # 获取配置中的logger对象
    # 输出logger日志记录
    logger.debug("====================【开始测试】====================")
    logger.info("====================【开始测试】====================")
    logger.warning("====================【开始测试】====================")
    logger.error("====================【开始测试】====================")
    logger.critical("====================【开始测试】====================")

输出结果:

2020-05-22 21:28:34 - logger1 - DEBUG - ====================【开始测试】====================
2020-05-22 21:28:34 - logger1 - INFO - ====================【开始测试】====================
2020-05-22 21:28:34 - logger1 - WARNING - ====================【开始测试】====================
2020-05-22 21:28:34 - logger1 - ERROR - ====================【开始测试】====================
2020-05-22 21:28:34 - logger1 - CRITICAL - ====================【开始测试】====================

九、其他方法

logging.basicConfig(**kwargs):该方法可设置多项参数,来创建StreamHandler或者FileHandler,并添加到根记录器中,从代码上来看,这个方法比常规的简洁

在这里插入图片描述述](https://img-blog.csdnimg.cn/20200521202640767.png)
在这里插入图片描述
实例:当有多个basicConfig存在,只有第一个才生效,后面的不会生效

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', style='%', level=logging.DEBUG)
# logging.basicConfig(filename='mylog1.log', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', style='%', level=logging.DEBUG) 

if __name__ == '__main__':
    logger = logging.getLogger(__name__)
    logger.debug("====================【开始测试】====================")

输出结果:

2020-05-21 20:31:17,495 - __main__ - DEBUG - ====================【开始测试】====================

实例

官网提供有专门的实例说明,有兴趣的可以看下:https://docs.python.org/3.8/howto/logging-cookbook.html

  • 55
    点赞
  • 275
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Python logging模块Python标准库中的一个模块,用于记录程序运行时的日志信息。它提供了一种简单的方法来记录代码的执行情况,以及任何可能会发生的错误或异常。Python logging模块可以将日志消息输出到控制台、文件、邮件等,同时还可以对日志进行级别、格式等的配置。 下面是一个简单的Python logging示例: ```python import logging # 配置日志级别和格式 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') # 输出不同级别的日志 logging.debug('This is a debug message') logging.info('This is an info message') logging.warning('This is a warning message') logging.error('This is an error message') logging.critical('This is a critical message') ``` 在这个示例中,我们首先配置了日志级别和格式,然后输出了不同级别的日志信息。日志级别从低到高依次为:DEBUG、INFO、WARNING、ERROR、CRITICAL。在这个示例中,我们将日志级别设置为DEBUG,因此所有级别的日志信息都会被输出。 输出结果如下: ``` 2021-10-28 10:10:10,123 - DEBUG - This is a debug message 2021-10-28 10:10:10,123 - INFO - This is an info message 2021-10-28 10:10:10,123 - WARNING - This is a warning message 2021-10-28 10:10:10,123 - ERROR - This is an error message 2021-10-28 10:10:10,123 - CRITICAL - This is a critical message ``` 从输出结果中,我们可以看到每条日志信息都包含了时间戳、日志级别和消息内容。这些信息可以帮助我们快速定位代码中的问题,以便进行调试和修复。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值