【Python】详解 logging 库

Python 的 logging 库是 Python 标准库中用于记录日志的模块,它提供了一种灵活且强大的方式来记录应用程序的日志信息。

1. 基本概念

在使用 logging 库之前,需要了解一些基本概念:

  • Logger:Logger 对象是应用程序中日志的入口点。每个 Logger 都有一个名称,可以通过名字获取 Logger 实例。

  • Handler:Handler 负责将日志记录发送到目的地,如控制台、文件等。

  • Formatter:Formatter 定义了日志记录的输出格式。

  • Filter:Filter 可以用于过滤日志记录,决定是否处理某条日志。

  • Level:日志级别,用于表示日志的严重程度,如 DEBUG、INFO、WARNING、ERROR、CRITICAL 等。

2. 基本用法

以下是一个简单的示例,演示如何使用 logging 库记录日志:

import logging

# 配置日志
logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 创建 Logger
logger = logging.getLogger('example_logger')

# 记录日志
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')

在这个示例中:

  • 使用 basicConfig 方法配置日志的基本设置,包括日志级别、日志文件名、文件模式和日志格式。

  • 创建一个名为 ‘example_logger’ 的 Logger 实例。

  • 使用不同级别的日志方法记录日志信息。

3. 配置日志文件

要将日志记录到文件中,可以使用 FileHandler 处理器。以下是一个更详细的配置示例:

import logging

# 创建 Logger
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)

# 创建 FileHandler,将日志写入文件
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 创建 Formatter,定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

# 将 FileHandler 添加到 Logger
logger.addHandler(file_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')

在这个示例中:

  • 创建了一个 Logger 实例,并设置其日志级别为 DEBUG。

  • 创建了一个 FileHandler,指定日志文件名为 ‘app.log’,并设置其日志级别为 DEBUG。

  • 创建了一个 Formatter,定义了日志的输出格式,并将其设置给 FileHandler。

  • 将 FileHandler 添加到 Logger 中。

  • 使用不同的日志级别记录日志信息,这些日志会被写入到 ‘app.log’ 文件中。

4. 配置控制台输出

除了文件输出,还可以配置日志输出到控制台。以下是一个示例:

import logging

# 创建 Logger
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)

# 创建 FileHandler,将日志写入文件
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 创建 Formatter,定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

# 创建 StreamHandler,将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

# 将 Handler 添加到 Logger
logger.addHandler(file_handler)
logger.addHandler(console_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')

在这个示例中:

  • 创建了一个 FileHandler 将日志写入文件。

  • 创建了一个 StreamHandler 将日志输出到控制台,并设置其日志级别为 INFO。

  • 将两个 Handler 都添加到 Logger 中。

  • 这样,日志会同时输出到文件和控制台,且控制台只显示 INFO 及以上级别的日志。

5. 高级配置

logging 库还支持更高级的配置方式,如使用字典配置、配置文件等。

5.1 使用字典配置

可以使用 dictConfig 方法通过字典来配置日志系统。以下是一个示例:

import logging
import logging.config

logging_config = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        }
    },
    'handlers': {
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'app.log',
            'level': 'DEBUG',
            'formatter': 'standard'
        },
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'INFO',
            'formatter': 'standard'
        }
    },
    'loggers': {
        'example_logger': {
            'level': 'DEBUG',
            'handlers': ['file', 'console'],
            'propagate': False
        }
    },
    'root': {
        'level': 'WARNING',
        'handlers': ['console']
    }
}

logging.config.dictConfig(logging_config)

logger = logging.getLogger('example_logger')

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')

在这个示例中:

  • 使用字典定义了日志配置,包括格式化器、处理器和日志器。

  • 通过 dictConfig 方法加载配置。

  • 创建了一个名为 ‘example_logger’ 的 Logger,配置了文件和控制台处理器。

  • 同时配置了根日志器,将 WARNING 及以上级别的日志输出到控制台。

5.2 使用配置文件

可以将日志配置保存在单独的配置文件中,然后通过 fileConfig 方法加载配置。以下是一个示例:

logging.config

[loggers]
keys=root,example_logger

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=standardFormatter

[logger_root]
level=WARNING
handlers=consoleHandler

[logger_example_logger]
level=DEBUG
handlers=fileHandler,consoleHandler
qualname=example_logger
propagate=0

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

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=standardFormatter
args=('app.log',)

[formatter_standardFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

Python代码

import logging
import logging.config

logging.config.fileConfig('logging.config')

logger = logging.getLogger('example_logger')

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')

在这个示例中:

  • 创建了一个配置文件 logging.config,定义了日志器、处理器和格式化器。

  • 通过 fileConfig 方法加载配置文件。

  • 创建了一个名为 ‘example_logger’ 的 Logger,配置了文件和控制台处理器。

6. 日志级别

日志级别用于表示日志的严重程度,常见的日志级别如下:

  • NOTSET:0,未设置,不记录任何日志。

  • DEBUG:10,详细信息,用于调试。

  • INFO:20,确认事物按预期工作。

  • WARNING:30,指示可能的问题,但不影响当前操作。

  • ERROR:40,由于较严重的问题,功能无法执行。

  • CRITICAL:50,严重错误,表明程序可能无法继续运行。

日志级别从低到高依次是 DEBUG < INFO < WARNING < ERROR < CRITICAL。

7. 日志记录器的继承

logging 库中,日志记录器支持继承。可以创建父日志记录器和子日志记录器,子记录器可以继承父记录器的设置。

例如:

import logging

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'INFO',
            'formatter': 'standard'
        }
    },
    'loggers': {
        'parent_logger': {
            'level': 'DEBUG',
            'handlers': ['console'],
            'propagate': True
        },
        'parent_logger.child_logger': {
            'level': 'INFO',
            'handlers': ['console'],
            'propagate': True
        }
    }
})

parent_logger = logging.getLogger('parent_logger')
child_logger = logging.getLogger('parent_logger.child_logger')

parent_logger.debug('Parent debug message')
child_logger.debug('Child debug message')

在这个示例中:

  • 创建了一个父日志记录器 ‘parent_logger’ 和一个子日志记录器 ‘parent_logger.child_logger’。

  • 子记录器继承了父记录器的设置,但可以覆盖自己的设置。

  • propagate 属性决定是否将日志消息传递给父记录器。

8. 日志的过滤

可以使用 Filter 对象来过滤日志记录,决定是否处理某条日志。以下是一个示例:

import logging

class InfoFilter(logging.Filter):
    def filter(self, record):
        return record.levelno == logging.INFO

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

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.addFilter(InfoFilter())

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

logger.addHandler(console_handler)

logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')

在这个示例中:

  • 创建了一个自定义的 Filter 类 InfoFilter,只允许 INFO 级别的日志通过。

  • 将这个 Filter 添加到 StreamHandler 中。

  • 因此,只有 INFO 级别的日志会输出到控制台。

9. 多进程日志记录

在多进程环境中,需要特别注意日志记录,以避免日志文件的竞态条件。可以使用 logging.handlers.QueueHandlerlogging.handlers.QueueListener 来解决这个问题。

以下是一个示例:

logger_config.py

import logging
import logging.handlers
import multiprocessing

def configure_logger():
    queue = multiprocessing.Manager().Queue(-1)
    queue_handler = logging.handlers.QueueHandler(queue)
    queue_listener = logging.handlers.QueueListener(queue, *logging.root.handlers)
    queue_listener.start()
    return queue_handler, queue_listener

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w',
                        format='%(asctime)s - %(processName)s - %(levelname)s - %(message)s')
    queue_handler, queue_listener = configure_logger()

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

    logger.debug('This is a debug message from the main process')

worker.py

import logging
import logging.handlers
import multiprocessing

def worker(logger):
    logger.debug('This is a debug message from a worker process')

if __name__ == "__main__":
    logging.config.fileConfig('logging.config')
    logger = logging.getLogger('example_logger')
    processes = []
    for _ in range(5):
        p = multiprocessing.Process(target=worker, args=(logger,))
        processes.append(p)
        p.start()
    for p in processes:
        p.join()

在这个示例中:

  • 在主进程中配置日志记录,并创建一个队列处理器和队列监听器。

  • 在 worker 进程中,使用配置好的日志记录器记录日志。

  • 通过队列处理器和队列监听器,确保日志消息能够正确地从 worker 进程传送到主进程的日志处理系统中。

10. 异常处理

在记录日志时,尤其是记录异常信息时,可以使用 exception 方法,它会自动捕获异常并记录 traceback 信息。

import logging

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

try:
    x = 1 / 0
except ZeroDivisionError:
    logger.exception('Division by zero error')

在这个示例中:

  • 当捕获到 ZeroDivisionError 异常时,使用 logger.exception 方法记录错误信息,同时会记录异常的 traceback 信息。

11. 日志轮转

在生产环境中,日志文件可能会变得非常大,因此需要日志轮转机制来管理日志文件的大小和数量。可以使用 logging.handlers.RotatingFileHandlerlogging.handlers.TimedRotatingFileHandler 来实现日志轮转。

11.1 RotatingFileHandler

按文件大小轮转:

import logging
import logging.handlers

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

handler = logging.handlers.RotatingFileHandler('app.log', maxBytes=1024*1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)

for _ in range(10000):
    logger.debug('This is a debug message')

在这个示例中:

  • 使用 RotatingFileHandler,当文件大小达到 1MB 时,会创建一个新的日志文件,最多保留5个备份文件。
11.2 TimedRotatingFileHandler

按时间轮转:

import logging
import logging.handlers

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

handler = logging.handlers.TimedRotatingFileHandler('app.log', when='midnight', interval=1, backupCount=7)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)

logger.debug('This is a debug message')

在这个示例中:

  • 使用 TimedRotatingFileHandler,每天 midnight 时创建一个新的日志文件,保留7天的备份文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值