【Python 日志记录:logging 模块入门】


文章目录

  • [Python 日志记录:logging 模块入门 📝](#Python 日志记录:logging 模块入门 📝)
    • [为什么需要日志记录? 🤔](#为什么需要日志记录? 🤔)
    • [基本用法 🚀](#基本用法 🚀)
    • [日志级别 📊](#日志级别 📊)
    • [高级配置 ⚙️](#高级配置 ⚙️)
    • [日志记录流程 🔄](#日志记录流程 🔄)
    • [实用技巧和最佳实践 💡](#实用技巧和最佳实践 💡)
      • [1. 使用不同的 Logger 实例](#1. 使用不同的 Logger 实例)
      • [2. 异常记录](#2. 异常记录)
      • [3. 性能考虑](#3. 性能考虑)
      • [4. 配置文件](#4. 配置文件)
    • [常见应用场景 🌐](#常见应用场景 🌐)
      • [1. Web 应用日志记录](#1. Web 应用日志记录)
      • [2. 长时间运行的任务监控](#2. 长时间运行的任务监控)
      • [3. 多模块项目](#3. 多模块项目)
    • [总结 🎉](#总结 🎉)

Python 日志记录:logging 模块入门 📝

日志记录是软件开发中不可或缺的一部分,它帮助我们追踪程序运行状态、调试问题以及监控应用程序的健康状况。Python 的 logging 模块提供了一个强大而灵活的日志记录系统,可以满足从简单脚本到复杂应用程序的各种需求。在本篇博客中,我们将深入探讨 logging 模块的基础知识、高级用法以及最佳实践,并通过丰富的代码示例和图表帮助你快速上手。

为什么需要日志记录? 🤔

在开发过程中,我们经常使用 print 语句来输出调试信息。然而,这种方法在生产环境中并不可靠,因为它缺乏灵活性、无法区分重要性级别,并且难以管理。logging 模块解决了这些问题,它允许你:

  • 区分不同严重级别的日志(如 DEBUG、INFO、WARNING、ERROR、CRITICAL)。
  • 将日志输出到多个目的地(如控制台、文件、网络等)。
  • 动态调整日志级别,无需修改代码。
  • 格式化日志消息,包含时间戳、模块名等有用信息。

根据 Python 官方文档,logging 模块的设计借鉴了 Log4j 等成熟日志库的经验,提供了高度可配置的日志记录解决方案。你可以访问 Python 官方 logging 模块文档 了解更多细节。

基本用法 🚀

让我们从最简单的例子开始。以下代码演示了如何配置和使用 logging 模块进行基本日志记录:

python 复制代码
import logging

# 配置根日志记录器,设置级别为 DEBUG
logging.basicConfig(level=logging.DEBUG)

# 记录不同级别的日志
logging.debug('这是一条调试信息')
logging.info('这是一条普通信息')
logging.warning('这是一条警告信息')
logging.error('这是一条错误信息')
logging.critical('这是一条严重错误信息')

运行上述代码,你会看到类似以下输出:

复制代码
DEBUG:root:这是一条调试信息
INFO:root:这是一条普通信息
WARNING:root:这是一条警告信息
ERROR:root:这是一条错误信息
CRITICAL:root:这是一条严重错误信息

默认情况下,basicConfig 将日志输出到控制台,并使用简单的格式。你可以通过参数自定义这些行为,例如:

python 复制代码
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.info('这是一条带格式的消息')

输出将类似于:

复制代码
2023-10-05 14:30:00 - root - INFO - 这是一条带格式的消息

日志级别 📊

logging 模块定义了以下日志级别,按严重性递增排列:

级别 数值 描述
DEBUG 10 详细信息,通常仅在调试时使用
INFO 20 确认程序按预期运行
WARNING 30 表示意外情况或潜在问题
ERROR 40 由于严重问题,程序无法执行某些功能
CRITICAL 50 严重错误,程序本身可能无法继续运行

你可以通过以下 mermaid 图表更直观地理解日志级别的层次结构:
#mermaid-svg-Ymw8mNxZM1BrM9pS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ymw8mNxZM1BrM9pS .error-icon{fill:#552222;}#mermaid-svg-Ymw8mNxZM1BrM9pS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ymw8mNxZM1BrM9pS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .marker.cross{stroke:#333333;}#mermaid-svg-Ymw8mNxZM1BrM9pS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ymw8mNxZM1BrM9pS p{margin:0;}#mermaid-svg-Ymw8mNxZM1BrM9pS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster-label text{fill:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster-label span{color:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster-label span p{background-color:transparent;}#mermaid-svg-Ymw8mNxZM1BrM9pS .label text,#mermaid-svg-Ymw8mNxZM1BrM9pS span{fill:#333;color:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .node rect,#mermaid-svg-Ymw8mNxZM1BrM9pS .node circle,#mermaid-svg-Ymw8mNxZM1BrM9pS .node ellipse,#mermaid-svg-Ymw8mNxZM1BrM9pS .node polygon,#mermaid-svg-Ymw8mNxZM1BrM9pS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .rough-node .label text,#mermaid-svg-Ymw8mNxZM1BrM9pS .node .label text,#mermaid-svg-Ymw8mNxZM1BrM9pS .image-shape .label,#mermaid-svg-Ymw8mNxZM1BrM9pS .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ymw8mNxZM1BrM9pS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .rough-node .label,#mermaid-svg-Ymw8mNxZM1BrM9pS .node .label,#mermaid-svg-Ymw8mNxZM1BrM9pS .image-shape .label,#mermaid-svg-Ymw8mNxZM1BrM9pS .icon-shape .label{text-align:center;}#mermaid-svg-Ymw8mNxZM1BrM9pS .node.clickable{cursor:pointer;}#mermaid-svg-Ymw8mNxZM1BrM9pS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .arrowheadPath{fill:#333333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ymw8mNxZM1BrM9pS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ymw8mNxZM1BrM9pS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ymw8mNxZM1BrM9pS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster text{fill:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS .cluster span{color:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ymw8mNxZM1BrM9pS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ymw8mNxZM1BrM9pS rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ymw8mNxZM1BrM9pS .icon-shape,#mermaid-svg-Ymw8mNxZM1BrM9pS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ymw8mNxZM1BrM9pS .icon-shape p,#mermaid-svg-Ymw8mNxZM1BrM9pS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ymw8mNxZM1BrM9pS .icon-shape .label rect,#mermaid-svg-Ymw8mNxZM1BrM9pS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ymw8mNxZM1BrM9pS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ymw8mNxZM1BrM9pS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ymw8mNxZM1BrM9pS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DEBUG: 详细信息
INFO: 常规信息
WARNING: 警告信息
ERROR: 错误信息
CRITICAL: 严重错误

设置日志级别可以过滤掉较低级别的消息。例如,如果设置级别为 WARNING,则只有 WARNINGERRORCRITICAL 消息会被记录。

高级配置 ⚙️

对于更复杂的应用,你可能需要更细粒度的控制。logging 模块提供了四大组件来实现这一目标:

  1. Loggers:暴露应用程序直接使用的接口。
  2. Handlers:将日志记录发送到适当的目的地。
  3. Filters:提供更精细的日志过滤功能。
  4. Formatters:指定日志输出的格式。

以下示例展示了如何创建和配置这些组件:

python 复制代码
import logging

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

# 创建控制台 handler 并设置级别为 DEBUG
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# 创建文件 handler 并设置级别为 ERROR
fh = logging.FileHandler('app.log')
fh.setLevel(logging.ERROR)

# 创建 formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 将 formatter 添加到 handler
ch.setFormatter(formatter)
fh.setFormatter(formatter)

# 将 handler 添加到 logger
logger.addHandler(ch)
logger.addHandler(fh)

# 记录一些消息
logger.debug('调试信息')
logger.info('普通信息')
logger.warning('警告信息')
logger.error('错误信息')
logger.critical('严重错误信息')

在这个例子中,DEBUG 和 INFO 消息只会输出到控制台,而 ERROR 和 CRITICAL 消息会同时输出到控制台和文件 app.log

日志记录流程 🔄

理解 logging 模块的工作流程对于有效使用它至关重要。以下 mermaid 序列图展示了一条日志消息的处理过程:
Destination Formatter Handler Filter Logger Destination Formatter Handler Filter Logger #mermaid-svg-PHVBaOGlQqkqCZ8K{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PHVBaOGlQqkqCZ8K .error-icon{fill:#552222;}#mermaid-svg-PHVBaOGlQqkqCZ8K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PHVBaOGlQqkqCZ8K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PHVBaOGlQqkqCZ8K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PHVBaOGlQqkqCZ8K .marker.cross{stroke:#333333;}#mermaid-svg-PHVBaOGlQqkqCZ8K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PHVBaOGlQqkqCZ8K p{margin:0;}#mermaid-svg-PHVBaOGlQqkqCZ8K .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PHVBaOGlQqkqCZ8K text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PHVBaOGlQqkqCZ8K .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PHVBaOGlQqkqCZ8K .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PHVBaOGlQqkqCZ8K #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PHVBaOGlQqkqCZ8K .sequenceNumber{fill:white;}#mermaid-svg-PHVBaOGlQqkqCZ8K #sequencenumber{fill:#333;}#mermaid-svg-PHVBaOGlQqkqCZ8K #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PHVBaOGlQqkqCZ8K .messageText{fill:#333;stroke:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PHVBaOGlQqkqCZ8K .labelText,#mermaid-svg-PHVBaOGlQqkqCZ8K .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .loopText,#mermaid-svg-PHVBaOGlQqkqCZ8K .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PHVBaOGlQqkqCZ8K .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PHVBaOGlQqkqCZ8K .noteText,#mermaid-svg-PHVBaOGlQqkqCZ8K .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PHVBaOGlQqkqCZ8K .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PHVBaOGlQqkqCZ8K .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PHVBaOGlQqkqCZ8K .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PHVBaOGlQqkqCZ8K .actorPopupMenu{position:absolute;}#mermaid-svg-PHVBaOGlQqkqCZ8K .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-PHVBaOGlQqkqCZ8K .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PHVBaOGlQqkqCZ8K .actor-man circle,#mermaid-svg-PHVBaOGlQqkqCZ8K line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PHVBaOGlQqkqCZ8K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建日志记录 过滤通过? 传递记录 格式化记录 输出到目标

  1. Logger 创建一条日志记录。
  2. Filter 决定是否丢弃该记录。
  3. 记录被传递给所有相关的 Handlers
  4. Handler 使用 Formatter 格式化记录。
  5. 格式化后的消息被发送到目标(如控制台或文件)。

实用技巧和最佳实践 💡

1. 使用不同的 Logger 实例

为每个模块创建独立的 Logger 实例,以便更好地组织日志:

python 复制代码
# 在模块中
import logging
logger = logging.getLogger(__name__)

def my_function():
    logger.info('函数被调用')

2. 异常记录

使用 exc_info 参数记录异常信息:

python 复制代码
try:
    # 可能出错的代码
    result = 1 / 0
except ZeroDivisionError:
    logger.error('除以零错误', exc_info=True)

3. 性能考虑

避免在调试语句中进行昂贵的计算,使用条件判断:

python 复制代码
if logger.isEnabledFor(logging.DEBUG):
    logger.debug(' expensive: %s', expensive_function())

4. 配置文件

对于生产环境,考虑使用配置文件或字典来配置 logging:

python 复制代码
import logging.config

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'detailed': {
            'format': '%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'INFO',
            'formatter': 'detailed'
        }
    },
    'root': {
        'level': 'DEBUG',
        'handlers': ['console']
    }
})

你可以在 Logging Cookbook 中找到更多高级配置示例。

常见应用场景 🌐

1. Web 应用日志记录

在 Web 应用中,你可能希望为每个请求记录特定的信息:

python 复制代码
import logging
from flask import Flask, request

app = Flask(__name__)
logger = logging.getLogger('webapp')

@app.route('/')
def index():
    logger.info('请求收到:%s', request.url)
    return 'Hello, World!'

2. 长时间运行的任务监控

对于长时间运行的任务,定期记录进度:

python 复制代码
import logging
import time

logger = logging.getLogger('long_task')

def long_running_task():
    for i in range(100):
        # 执行任务
        time.sleep(1)
        if i % 10 == 0:
            logger.info('进度:%d%%', i)

3. 多模块项目

在大型项目中,为每个模块配置适当的日志记录:

python 复制代码
# module1.py
import logging
logger = logging.getLogger('project.module1')

# module2.py
import logging
logger = logging.getLogger('project.module2')

总结 🎉

Python 的 logging 模块是一个强大而灵活的工具,可以大大改善你的应用程序的可观察性和可维护性。通过本指南,你应该已经掌握了:

  • 基本的日志记录配置和使用
  • 日志级别的重要性
  • 高级组件(Logger、Handler、Formatter、Filter)的使用
  • 一些最佳实践和常见应用场景

记住,良好的日志记录实践不仅有助于调试,还能提供 valuable insights into your application's behavior in production. 要深入了解,请参考 Advanced Logging Tutorial

开始将 logging 模块集成到你的项目中吧,你会发现它比 print 语句强大得多!Happy logging! 🚀

相关推荐
xcLeigh1 小时前
Go入门:变量声明的五种方式详解
java·开发语言·golang
zmzb01032 小时前
C++课后习题训练记录Day175
开发语言·c++
脱胎换骨-军哥3 小时前
C++ 代码规范与格式化指南
开发语言·c++·代码规范
枕星而眠3 小时前
C++ STL Map容器完全指南:从有序红黑树到无序哈希表
java·开发语言
lipku3 小时前
数字人直播开源项目LiveStream
python·开源·数字人·数字人直播
阿童木写作4 小时前
Python实现亚马逊商品图批量智能抠图教程
人工智能·python
爱吃牛肉的大老虎4 小时前
Rust对象之结构体,枚举,特性
开发语言·后端·rust
axinawang4 小时前
第24课:while循环的应用
python
bbq粉刷匠4 小时前
HashMap 底层原理深度拆解(二):putVal 完整链路解析(懒加载 · 链表遍历 · 尾插法)
java·开发语言·哈希算法