Python编程-使用logging管理程序日志

Python编程-使用logging管理程序日志

自动化运维时时常用到,故抽时间专门总结

Python的logging模块是Python内置的日志记录工具,用于在程序中实现灵活的日志记录功能。使用logging模块可以记录程序运行时的各种信息,如调试信息、警告、错误等,以便在需要时进行排查和分析。

Python的logging模块定义了5个日志级别,用于指示日志消息的重要程度,从低到高分别为:

  1. DEBUG:最详细的日志级别,通常用于调试目的,记录详细的系统状态信息。对于生产环境,应该将DEBUG日志级别关闭,以避免记录过多的信息。
  2. INFO:用于记录程序的正常操作信息,例如启动信息、关闭信息等。对于生产环境,INFO级别是默认的日志级别,可以提供足够的信息来了解系统的运行情况。
  3. WARNING:警告级别,表示可能出现的问题,但不会影响系统的正常运行。例如,使用了过期的函数或者接口,但是系统仍然能够正常工作。
  4. ERROR:错误级别,表示出现了一些问题,但不会导致系统崩溃。例如,文件无法读取、网络连接失败等。
  5. CRITICAL:最高级别的日志级别,表示严重问题,可能会导致系统崩溃或无法继续运行。例如,关键文件丢失、关键服务停止等。

需要注意的是,logging允许我们自定义日志级别,但是最好不要这样操作,有可能导致日志级别出现混乱,加大维护与管理的难度

日志处理模块的四大组件

  1. Logger(日志器):Logger对象用于创建和记录日志消息。可以通过logging.getLogger(name)方法获取Logger对象,其中name参数为日志器的名称。每个Logger对象都可以设置自己的日志级别和处理器,用于控制日志消息的输出。
  2. Handler(处理器):Handler对象用于将日志消息发送到指定的位置,如控制台、文件、网络等。可以通过logging.StreamHandlerlogging.FileHandler等类创建不同类型的处理器。每个Handler对象都可以设置自己的日志级别和格式,用于控制输出的细节。
  3. Formatter(格式器):Formatter对象用于指定日志消息的输出格式。可以通过logging.Formatter类创建格式化器,并将其应用到Handler对象中。可以自定义输出的格式,包括时间、日志级别、日志名称、日志消息等信息。
  4. Filter(过滤器):Filter对象用于过滤日志消息,只有符合条件的日志消息才会被处理和输出。可以通过继承logging.Filter类创建自定义的过滤器,并将其应用到Logger或Handler对象中。过滤器可以根据日志消息的属性(如级别、名称、消息内容等)进行过滤。

日志器Logger使用案例

日志器的创建

日志器的创建一个是通过其本身的构造器创建,它接收两个参数,一个是日志器名称,一个是日志器最低等级。Logger的构造器中的level参数用于设置日志器的最低级别(或者说最低优先级),低于该级别的日志消息将被忽略。level参数的类型是整数,对应着不同的日志级别,如下所示:

  • logging.DEBUG(整数值为10):调试信息级别,最低的日志级别。
  • logging.INFO(整数值为20):信息消息级别。
  • logging.WARNING(整数值为30):警告消息级别。
  • logging.ERROR(整数值为40):错误消息级别。
  • logging.CRITICAL(整数值为50):严重错误消息级别,最高的日志级别。

通过设置level参数,可以控制日志记录器记录哪些级别以上的日志消息。例如,如果将level参数设置为logging.WARNING,则只有警告级别及以上的日志消息才会被记录,而调试和信息级别的消息将被忽略。

import logging

logging.Logger(name='my_logger', level=logging.DEBUG)

一个是通过getLogger([name])来进行创建,它只接受日志器的名称

import logging

logging.getLogger(name='my_logger')  

注意:在未指定日志级别时,默认为Warning级别日志

日志器的常用方法

方法描述
debug(msg, *args, **kwargs)记录调试级别的日志消息。
info(msg, *args, **kwargs)记录信息级别的日志消息。
warning(msg, *args, **kwargs)记录警告级别的日志消息。
error(msg, *args, **kwargs)记录错误级别的日志消息。
critical(msg, *args, **kwargs)记录严重级别的日志消息。
exception(msg, *args, **kwargs)类似于error()方法,但是会同时记录异常信息。
log(level, msg, *args, **kwargs)记录指定级别的日志消息。
setLevel(level)设置日志记录的最低级别。
addHandler(hdlr)添加日志处理器(handler)到日志器中。
removeHandler(hdlr)从日志器中移除指定的日志处理器。
setFormatter(fmt)设置日志消息的格式化器(formatter)。
hasHandlers()检查日志器是否有任何处理器。

创建并添加日志

import logging

# 创建Logger对象
my_logger = logging.getLogger('my_logger')
my_logger.setLevel(logging.DEBUG)

# 添加五种不同级别的日志消息
my_logger.debug('This is a DEBUG message')
my_logger.info('This is an INFO message')
my_logger.warning('This is a WARNING message')
my_logger.error('This is an ERROR message')
my_logger.critical('This is a CRITICAL message')

注意:日志器是一个单例模式,在未指定name时,多次处理均为同一个logger对象,要解决这个问题可以运行结束后移除处理器,或者对每次的日志器进行不同的命名(网上看到有人在提,不给过个人觉得一般不会这样写程序)

import logging

def self_define_log() -> logging.Logger:
 logger = logging.getLogger()
 logger.setLevel(logging.DEBUG)
 console_handler = logging.StreamHandler()
 console_handler.setLevel(logging.DEBUG)
 logger.addHandler(console_handler)
 return logger

if __name__ == "__main__":
 self_define_log().info('This is an INFO message')
 self_define_log().debug('This is a DEBUG message')

我们将会得到输出结果:

This is an INFO message
This is a DEBUG message
This is a DEBUG message

日志处理器Handler使用案例

日志处理器需要通过Logger的addHandler方法来进行添加,我们给出常用方法表进行展示:

方法描述
setLevel(level)设置处理器将处理的日志消息的最低级别。
setFormatter(formatter)为处理器设置日志消息的格式化方式。
addFilter(filter)添加过滤器,过滤器可以根据特定的条件决定是否处理日志消息。
removeFilter(filter)移除指定的过滤器。

但是我们使用它的时候实质上使用的是它的子类对象:

子类对象描述
StreamHandler将日志消息输出到流(通常是sys.stdout或sys.stderr)。
FileHandler将日志消息写入到文件中。
RotatingFileHandler将日志消息写入到文件中,并可以在达到一定大小或时间间隔时自动切割文件。
TimedRotatingFileHandler类似于RotatingFileHandler,但是根据时间间隔来切割文件,比如每天、每周或每月。
SocketHandler将日志消息发送到网络套接字。
SMTPHandler将日志消息通过电子邮件发送。
MemoryHandler将日志消息存储在内存中,可以设置当达到一定数量或大小时将日志消息写入到其他处理器(如FileHandler)中。
QueueHandler将日志消息放入队列中,可以与多个处理器共享一个队列,用于实现多线程日志记录。
NullHandler空处理器,用于禁用日志记录。
HTTPHandler将日志消息发送到远程HTTP服务器。

将日志输出到控制台

import logging
import logging.handlers


logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
logger.addHandler(stream_handler)

logger.debug('This is a DEBUG message')
logger.info('This is an INFO message')
logger.warning('This is a WARNING message')
logger.error('This is an ERROR message')
logger.critical('This is a CRITICAL message')

要输出DEBUGINFO级别的日志消息,需要在添加处理程序时将其级别设置为相应的级别。默认情况下,StreamHandler的级别是WARNING,因此它将忽略DEBUGINFO级别的消息。要输出这些级别的消息,需要在创建StreamHandler时将其级别设置为DEBUG或更低级别。

将日志发送到指定服务器

import logging
import logging.handlers

logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)

# 创建HTTPHandler,指定远程HTTP服务器的URL和HTTP方法
http_handler = logging.handlers.HTTPHandler('localhost:8000', '/log', method='POST')
http_handler.setLevel(logging.DEBUG)
logger.addHandler(http_handler)

logger.debug('This is a DEBUG message sent via HTTP')
logger.info('This is an INFO message sent via HTTP')
logger.warning('This is a WARNING message sent via HTTP')
logger.error('This is an ERROR message sent via HTTP')
logger.critical('This is a CRITICAL message sent via HTTP')

日志过滤器使用案例

在使用日志过滤器时,我们需要自定义一个Filter类,在该类中重写filter方法,来自定义过滤条件:

  1. 基于Logger名称过滤:可以根据Logger的名称来过滤日志记录,只输出特定名称的Logger的日志记录。
class CustomFilter(logging.Filter):
    def filter(self, record):
        return record.name == 'example'

filter_instance = CustomFilter()
logger.addFilter(filter_instance)
  1. 基于日志消息内容过滤:可以根据日志消息的内容来过滤日志记录,只输出包含特定内容的日志记录。
class CustomFilter(logging.Filter):
    def filter(self, record):
        return 'error' in record.msg.lower()

filter_instance = CustomFilter()
logger.addFilter(filter_instance)
  1. 基于创建线程过滤:可以根据其他条件来过滤日志记录,比如根据日志记录的创建时间、线程名等。
class CustomFilter(logging.Filter):
    def filter(self, record):
        return record.threadName == 'MainThread'

filter_instance = CustomFilter()
logger.addFilter(filter_instance)
  1. 基于日志等级过滤:指定日志的等级条件来过滤日志
class CustomFilter(logging.Filter):
    def filter(self, record):
        return record.levelno == logging.INFO
filter_instance = CustomFilter()
logger.addFilter(filter_instance)

日志格式器使用案例

格式器的构造方式

格式器的构造器接受五个参数,依次是输出消息的格式,输出日期时的格式,日志风格字符,另外两个参数是后来新加的:

  • validate:如果为 True,则不正确或不匹配的 fmtstyle 将引发 ValueError。默认为 True。
  • defaults:一个字典,包含在自定义字段中使用的默认值。例如 logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})
def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *,
                 defaults=None):
    if style not in _STYLES:
            raise ValueError('Style must be one of: %s' % ','.join(
                             _STYLES.keys()))
        self._style = _STYLES[style][0](fmt, defaults=defaults)
        if validate:
            self._style.validate()

        self._fmt = self._style._fmt
        self.datefmt = datefmt

我们来看简单的使用示例:

import logging

logger = logging.getLogger('example')
logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
logger.addHandler(console_handler)

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

logger.debug('This is a DEBUG message')
logger.info('This is an INFO message')
logger.warning('This is a WARNING message')
logger.error('This is an ERROR message')
logger.critical('This is a CRITICAL message')

style 参数指定了格式字符串的风格,可以是 %{$ 之一,决定了格式字符串将如何与数据合并。以下是每种风格的用法:

  • %:使用 printf 风格的字符串格式化。例如,logging.Formatter('%(asctime)s - %(message)s')
  • {:使用 str.format()。例如,logging.Formatter('{asctime} - {message}')
  • $:使用 string.Template。例如,logging.Formatter('$asctime - $message')

格式器的内容指定符

格式化字符串描述
%(name)s日志记录器的名称(日志通道)
%(levelno)s消息的数字日志级别(DEBUG、INFO、WARNING、ERROR、CRITICAL)
%(levelname)s消息的文本日志级别(“DEBUG”、“INFO”、“WARNING”、“ERROR”、“CRITICAL”)
%(pathname)s发出日志调用的源文件的完整路径名(如果可用)
%(filename)s路径名的文件名部分
%(module)s模块(文件名的名称部分)
%(lineno)d发出日志调用的源行号(如果可用)
%(funcName)s函数名
%(created)f创建 LogRecord 的时间(time.time() 的返回值)
%(asctime)sLogRecord 创建时的文本时间
%(msecs)d创建时间的毫秒部分
%(relativeCreated)d创建 LogRecord 时的时间(相对于加载日志模块的时间,通常为应用程序启动时间)的毫秒数
%(thread)d线程 ID(如果可用)
%(threadName)s线程名称(如果可用)
%(process)d进程 ID(如果可用)
%(message)s记录.getMessage() 的结果,即记录被发出时计算的消息内容

日志器统一配置添加

基本参数配置

logging.basicConfig 方法用于配置默认的日志记录器的基本配置,例如日志级别、输出格式和处理程序。如果在应用程序中没有显式配置日志系统,则会使用默认配置。该方法的签名如下:

logging.basicConfig(**kwargs)

参数说明:

  • filename:要将日志消息写入的文件名称。
  • filemode:写入文件时使用的模式,默认为 'a'(追加)。如果指定了 filename,则此参数有效。
  • format:用于设置日志消息的格式。
  • datefmt:用于设置日期/时间部分的格式。
  • level:设置日志记录器的级别。
  • stream:指定一个流(例如 sys.stdoutsys.stderr),用于输出日志消息。如果指定了 filename,则 stream 参数无效。
  • style:设置格式化字符串的风格,默认为 '%'
  • handlers:设置默认的处理器

例如,要将日志消息记录到文件中,并设置日志级别为 DEBUG,可以使用以下代码:

import logging

logging.basicConfig(filename='example_log.log', level=logging.DEBUG)

这将导致日志消息被写入到名为 example.log 的文件中,并且只有 DEBUG 级别或更高级别的消息会被记录。

需要注意的是filenamestream参数不能够同时设置,两者定义的都是输出流,将会引发ValueError

可迭代处理器

logging.basicConfig 方法还可以接受一个 handlers 参数,用于指定要添加到根记录器的处理程序列表。处理程序用于定义将日志消息发送到的位置,例如控制台、文件、网络等。

以下是一个示例,将日志同时输出到控制台和文件中,handlers列表中的参数将依次运行:

import logging

# 创建一个日志记录器
logger = logging.getLogger(name='my_logger')
logger.setLevel(logging.DEBUG)

# 创建一个控制台处理程序,将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 创建一个文件处理程序,将日志写入到文件中
file_handler = logging.FileHandler('example.log')
file_handler.setLevel(logging.DEBUG)

# 将处理程序添加到日志记录器中,并进行格式化
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    handlers=[file_handler, console_handler])

# 记录日志消息
logger.info('This is an info message')
logger.debug('This is a debug message')
logger.log(level=logging.WARNING, msg='This is a warning message')

这里需要注意的是,写日志的方法也可以脱离Logger对象使用:

logger.info('This is an info message')
logger.debug('This is a debug message')
logger.log(level=logging.WARNING, msg='This is a warning message')

日志模块配置分离

logging.config 模块提供了用于配置 Python 日志系统的功能。它允许您从配置文件、字典或其他来源加载日志配置,并将其应用于日志记录器。

主要的函数和类包括:

  • dictConfig(config):从字典配置日志系统。字典可以包含处理程序、格式化器和日志记录器的配置信息。
  • fileConfig(fname, defaults=None, disable_existing_loggers=True):从配置文件配置日志系统。配置文件的格式可以是 INI 格式或 JSON 格式。
  • listen(port):在给定端口上启动一个网络日志监听器,用于接收远程日志记录请求,但是这个有一定安全风险。
  • stopListening():停止网络日志监听器。
  • threading.Thread(target=handleLogRecords):一个线程类,用于处理接收到的日志记录。

其实我们常用的还是fileConfig,所以这里我也仅介绍此方式

配置日志器config

在编写config文件时,我们默认使用的是ini风格:

[loggers]
keys=root,exampleLogger

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_exampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=logger1
propagate=0
  • [loggers]:这个部分定义了所有的logger,并使用keys关键字指定了所有logger的名称。需要注意的是,我们必须定义root日志器,以保证全局配置完整

  • logger_xxx:用于单独配置每个logger

    • level=DEBUG:设置日志级别DEBUG
    • handlers=consoleHandler:指定了处理器为consoleHandler
    • qualname=logger1:指定了logger的名称为logger1。这个名称将在代码中使用,用于获取对应的logger对象。
    • propagate=0:禁止消息传播到父logger。如果不禁用传播,将会在打印日志输出两份相同日志,一个传递到父日志器,一个传递给当前日志器

还有个与propagate相关的属性:parent属性用于指定父日志器,在未声明情况下默认为root日志器

配置处理器config

[handlers]
keys=consoleHandler

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
  • [handlers]:这个部分定义了所有的handlers,并使用keys关键字指定了所有handlers的名称。

  • handlers_xxx:用于单独配置每个handlers

    • class=StreamHandler:设置处理器为StreamHandler
    • level=DEBUG:设置处理器级别DEBUG
    • formatter=simpleFormatter:指定了格式器为simpleFormatter
    • args=(sys.stdout,):指定了处理器的接受参数为(sys.stdout, )元组

配置格式器config

[formatters]
keys=simpleFormatter

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

这里就不过多赘述了,只需要注意format控制的是输出msg的格式,datefmt控制的是日期格式,style控制格式风格即可

使用配置文件

import logging
from logging.config import fileConfig

# 加载配置文件
fileConfig('config.ini')

# 获取名为 'logger1' 的日志记录器
logger1 = logging.getLogger('logger1')
print(logger1.getEffectiveLevel())
logger1.debug('This is a debug message from logger1')
logger1.info('This is an info message from logger1')

需要注意的是,尽管使用了配置文件,但是我们仍然可以获取配置文件以外的日志器,并且这些配置文件外的日志器默认继承配置文件中的root日志器

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值