Python Logging 模块深度全解:从入门到生产环境 万字实战指南

AgenticCoding·十二月创作之星挑战赛 10w+人浏览 535人参与

博客引言

2023 年我负责的某电商系统线上故障排查,让我深刻体会到「Python Logging 模块是开发中最容易被轻视、却最影响线上稳定性的组件」:

当时系统突然出现订单支付失败,但监控系统未报警,服务器也无异常指标。我们花了 4 小时排查,才发现开发人员用print()代替了logging输出支付日志,而容器重启后print日志被清空,导致完全无法定位根因。

类似的「日志配置错误导致故障定位困难」「日志太多导致磁盘爆满」「多模块日志混乱」等问题,在 Python 开发者中比比皆是。本文将用15000字 + 的篇幅,从基础组件、配置方式、进阶特性、生产实战、踩坑避坑、最佳实践6 大维度,彻底讲透 Python Logging 模块的所有核心知识点与落地经验。


第一章 Python Logging 模块核心组件与基础概念

1.1 Logging 模块的 4 大核心组件

Logging 模块的本质是「日志生产 - 过滤 - 格式化 - 输出」的流水线,核心由 4 个组件构成:

  • Logger(日志器):日志的「生产入口」,负责生成日志、设置日志级别、传递日志到 Handler;
  • Handler(处理器):日志的「输出目标」,负责将日志发送到控制台、文件、网络等;
  • Formatter(格式化器):日志的「样式定义」,负责将日志记录格式化为可读字符串;
  • Filter(过滤器):日志的「过滤规则」,负责按条件过滤日志(比级别更灵活)。

1.2 日志级别体系

Logging 模块定义了 5 种递增的日志级别,用于控制日志的输出粒度(默认只输出 WARNING 及以上级别):

级别名称数值含义
DEBUG10调试信息(开发阶段)
INFO20常规信息(运行状态)
WARNING30警告信息(潜在问题)
ERROR40错误信息(功能异常)
CRITICAL50致命信息(系统崩溃)

1.3 模块级函数的陷阱(新手必踩)

很多新手会直接使用 Logging 模块的模块级函数(如logging.info()),但这隐藏了巨大的陷阱:

# 新手踩坑代码:直接调用模块级函数
import logging
logging.info("这是一条INFO日志")  # 无任何输出!
logging.warning("这是一条WARNING日志")  # 输出:WARNING:root:这是一条WARNING日志

陷阱原因

  1. 模块级函数默认使用「根 Logger(名称为root)」;
  2. 根 Logger 的默认级别是WARNING,因此INFO级别日志会被过滤;
  3. 根 Logger 默认只配置了StreamHandler(输出到控制台),且无格式化。

1.4 正确的基础使用姿势

使用 Logging 模块的标准流程是:

  1. 创建 / 获取 Logger 实例(用__name__作为 Logger 名称,自动继承模块层级);
  2. 配置 Handler(指定输出目标、级别、格式化);
  3. 将 Handler 添加到 Logger;
  4. 生成日志。
# 标准基础使用代码
import logging

# 1. 创建/获取Logger(名称为当前模块名,如"__main__")
logger = logging.getLogger(__name__)
# 设置Logger级别为DEBUG(默认是NOTSET,继承父Logger的级别)
logger.setLevel(logging.DEBUG)

# 2. 配置Handler:输出到控制台的StreamHandler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)  # Handler级别:仅输出INFO及以上

# 3. 配置Formatter:日志格式包含时间、模块名、级别、内容
console_formatter = logging.Formatter(
    "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
console_handler.setFormatter(console_formatter)

# 4. 将Handler添加到Logger
logger.addHandler(console_handler)

# 5. 生成日志
logger.debug("这是DEBUG日志(不会输出到控制台)")  # 级别低于Handler的INFO
logger.info("这是INFO日志(会输出到控制台)")  # 符合Handler级别
logger.warning("这是WARNING日志(会输出到控制台)")  # 符合Handler级别

运行结果

2024-05-20 15:30:00,123 - __main__ - INFO - 这是INFO日志
2024-05-20 15:30:00,124 - __main__ - WARNING - 这是WARNING日志

1.5 核心组件的继承关系

Logger 的名称支持层级结构(用.分隔),如app.module1app的子 Logger,app是根 Logger 的子 Logger。继承规则如下:

  1. 子 Logger 默认继承父 Logger 的级别、Handler、Filter
  2. 若子 Logger 设置了自己的级别,则不再继承父 Logger 的级别;
  3. 若子 Logger 的propagate属性设为False,则不再将日志传播给父 Logger 的 Handler。

第二章 Logging 模块的配置方式

Logging 模块支持4 种主流配置方式,其中「字典配置(dictConfig)」是生产环境的推荐方式:

2.1 基础配置函数:basicConfig()

basicConfig()是 Logging 模块提供的快速配置根 Logger的函数,适合开发调试阶段:

import logging
import logging.handlers

# 配置根Logger:输出到文件+控制台,同时开启日志滚动
logging.basicConfig(
    level=logging.DEBUG,  # 根Logger级别
    format="%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s",  # 日志格式
    datefmt="%Y-%m-%d %H:%M:%S",  # 时间格式
    handlers=[
        logging.FileHandler("app.log", mode="a", encoding="utf-8"),  # 输出到文件(追加模式)
        logging.StreamHandler(),  # 输出到控制台
        logging.handlers.RotatingFileHandler(  # 日志滚动:最大100MB,保留10个备份
            "app_roll.log", maxBytes=100*1024*1024, backupCount=10, encoding="utf-8"
        )
    ]
)

# 测试日志
logger = logging.getLogger(__name__)
logger.debug("这是DEBUG日志")
logger.info("这是INFO日志")
logger.error("这是ERROR日志")

basicConfig()的限制

  1. 仅能配置根 Logger,无法配置子 Logger;
  2. 只能配置一次,第二次调用会被忽略;
  3. 无法灵活配置多个 Handler 的级别和过滤器。

2.2 文件配置:fileConfig()

fileConfig()支持从INI 格式文件读取配置,适合需要动态修改配置的场景:

# logging_config.ini(INI格式配置文件)
[loggers]
keys=root,app,lib

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter,detailedFormatter

# 根Logger配置
[logger_root]
level=DEBUG
handlers=consoleHandler

# app模块Logger配置
[logger_app]
level=INFO
handlers=fileHandler
qualname=app  # Logger名称
propagate=0  # 不传播给父Logger

# lib模块Logger配置
[logger_lib]
level=WARNING
handlers=fileHandler
qualname=lib
propagate=0

# 控制台Handler配置
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)

# 文件Handler配置
[handler_fileHandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=detailedFormatter
args=("app.log", "a", 100*1024*1024, 10, "utf-8")

# 简单格式
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

# 详细格式
[formatter_detailedFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(process)d:%(threadName)s - %(module)s:%(funcName)s:%(lineno)d - %(message)s
# 加载INI配置文件
import logging
import logging.config

logging.config.fileConfig("logging_config.ini")
logger = logging.getLogger("app")
logger.info("app模块的INFO日志")

2.3 字典配置:dictConfig()(生产环境推荐)

dictConfig()支持从Python 字典JSON/YAML 文件读取配置,是 Logging 模块最灵活、最适合生产环境的配置方式。以下是一个生产级完整配置示例

import logging.config
import os
import uuid
from flask import g, request

# 1. 自定义请求ID过滤器(用于Web应用追踪请求)
class RequestIdFilter(logging.Filter):
    def filter(self, record):
        # 从Flask的g对象中获取请求ID,若没有则生成
        record.request_id = getattr(g, "request_id", uuid.uuid4().hex)
        return True

# 2. 生产级字典配置
LOGGING_CONFIG = {
    "version": 1,  # 配置版本(固定为1)
    "disable_existing_loggers": False,  # 不禁用已存在的Logger
    "formatters": {
        # 控制台日志格式
        "console": {
            "format": "%(asctime)s - %(request_id)s - %(name)s - %(levelname)s - %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S"
        },
        # 文件日志格式(包含进程/线程/模块/行号)
        "file": {
            "format": "%(asctime)s - %(request_id)s - %(name)s - %(levelname)s - PID:%(process)d TID:%(threadName)s - %(module)s:%(funcName)s:%(lineno)d - %(message)s"
        }
    },
    "filters": {
        # 注册自定义请求ID过滤器
        "request_id": {"()": lambda: RequestIdFilter()}
    },
    "handlers": {
        # 控制台Handler(仅输出INFO及以上)
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "console",
            "filters": ["request_id"]
        },
        # 错误日志Handler(仅输出ERROR及以上,单独存储)
        "error_file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "ERROR",
            "formatter": "file",
            "filename": os.path.join("logs", "error.log"),
            "maxBytes": 100 * 1024 * 1024,  # 100MB
            "backupCount": 20,  # 保留20个备份
            "encoding": "utf-8",
            "delay": True  # 延迟创建文件(避免多进程竞争)
        },
        # 所有日志Handler(输出DEBUG及以上,滚动存储)
        "all_file": {
            "class": "logging.handlers.TimedRotatingFileHandler",
            "level": "DEBUG",
            "formatter": "file",
            "filename": os.path.join("logs", "app.log"),
            "when": "D",  # 每天滚动
            "interval": 1,  # 滚动间隔1天
            "backupCount": 30,  # 保留30天日志
            "encoding": "utf-8",
            "atTime": "00:00:00"  # 每天零点滚动
        },
        # Syslog Handler(输出到日志服务器,用于监控)
        "syslog": {
            "class": "logging.handlers.SysLogHandler",
            "level": "WARNING",
            "formatter": "console",
            "address": ("192.168.1.100", 514),  # 日志服务器地址
            "facility": logging.handlers.SysLogHandler.LOG_USER
        }
    },
    "loggers": {
        # 根Logger
        "": {
            "level": "DEBUG",
            "handlers": ["console", "all_file", "error_file"]
        },
        # 应用模块Logger(如Flask的app)
        "app": {
            "level": "INFO",
            "handlers": ["syslog"],
            "propagate": True  # 日志传播到根Logger
        },
        # 第三方库Logger(如requests)
        "requests": {
            "level": "WARNING",  # 屏蔽requests的DEBUG日志
            "handlers": ["all_file"],
            "propagate": False
        },
        # 数据库模块Logger
        "sqlalchemy": {
            "level": "WARNING",  # 屏蔽SQLAlchemy的DEBUG日志
            "handlers": ["all_file"],
            "propagate": False
        }
    }
}

# 3. 加载字典配置
logging.config.dictConfig(LOGGING_CONFIG)

dictConfig()的优势

  1. 支持配置所有组件(Logger、Handler、Formatter、Filter);
  2. 支持自定义组件(如请求 ID 过滤器);
  3. 可通过 JSON/YAML 文件动态修改配置;
  4. 支持多环境配置(开发 / 测试 / 生产)。

第三章 Logging 模块进阶特性

3.1 日志滚动机制(生产环境必须配置)

日志文件若不滚动,会导致磁盘空间耗尽,Logging 模块提供了 2 种滚动 Handler:

3.1.1 大小滚动:RotatingFileHandler

当日志文件达到指定大小后,自动创建新文件(文件名加数字后缀,如app.log.1):

import logging.handlers
from logging import Formatter

logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)

# 配置大小滚动Handler:最大100MB,保留10个备份
rotating_handler = logging.handlers.RotatingFileHandler(
    filename="app.log",
    maxBytes=100*1024*1024,  # 单个日志文件最大大小
    backupCount=10,  # 保留的备份文件数
    encoding="utf-8",
    delay=True  # 延迟创建文件,避免多进程竞争
)
rotating_handler.setFormatter(Formatter("%(asctime)s - %(message)s"))
logger.addHandler(rotating_handler)

3.1.2 时间滚动:TimedRotatingFileHandler

按时间间隔自动创建新文件(支持年 / 月 / 日 / 时 / 分 / 秒):

import logging.handlers
from datetime import time

logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)

# 配置时间滚动Handler:每天零点滚动,保留30天
timed_handler = logging.handlers.TimedRotatingFileHandler(
    filename="app.log",
    when="D",  # 滚动间隔:Y=年,M=月,D=日,H=时,m=分,s=秒
    interval=1,  # 间隔倍数(如when="H",interval=6则每6小时滚动)
    backupCount=30,  # 保留30天日志
    encoding="utf-8",
    atTime=time(0, 0, 0)  # 滚动时间点(每天零点)
)
# 配置文件名后缀:%Y-%m-%d → app.log.2024-05-20
timed_handler.suffix = "%Y-%m-%d"
timed_handler.setFormatter(Formatter("%(asctime)s - %(message)s"))
logger.addHandler(timed_handler)

3.2 自定义过滤器(Filter)

过滤器可以实现比级别更灵活的日志过滤,如按日志内容、模块名、请求 ID 等过滤:

3.2.1 按模块名过滤

# 只允许来自app模块的日志通过
class ModuleFilter(logging.Filter):
    def __init__(self, module_name):
        self.module_name = module_name
    
    def filter(self, record):
        # record.name是Logger的名称
        return record.name.startswith(self.module_name)

# 使用过滤器
logger = logging.getLogger("app")
filter = ModuleFilter("app")
handler.addFilter(filter)

3.2.2 按日志内容过滤

# 只允许包含"pay"关键字的日志通过(用于过滤支付相关日志)
class ContentFilter(logging.Filter):
    def __init__(self, keyword):
        self.keyword = keyword
    
    def filter(self, record):
        return self.keyword in record.getMessage()

3.3 日志上下文信息(LoggerAdapter)

在 Web 应用或异步场景中,需要将请求 ID、用户 ID、设备 ID等上下文信息添加到所有日志中,LoggerAdapter可以实现这一点:

import logging
import uuid

# 创建基础Logger
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s - %(request_id)s - %(user_id)s - %(message)s"))
logger.addHandler(handler)

# 1. 创建LoggerAdapter,添加上下文信息
context = {"request_id": uuid.uuid4().hex, "user_id": "12345"}
adapter = logging.LoggerAdapter(logger, extra=context)

# 2. 生成带上下文的日志
adapter.info("用户登录成功")  # 输出:2024-05-20 16:00:00 - xxxxxxx - 12345 - 用户登录成功
adapter.warning("用户登录失败")  # 输出带上下文的警告日志

# 3. 动态更新上下文信息
adapter.extra["user_id"] = "67890"
adapter.info("用户修改密码")  # 输出:2024-05-20 16:00:01 - xxxxxxx - 67890 - 用户修改密码

3.4 日志的异常捕获

Logging 模块的 **exception()方法可以自动捕获当前异常并打印堆栈信息 **,是排查错误的利器:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
logger.addHandler(logging.StreamHandler())

try:
    # 模拟异常
    a = 1 / 0
except Exception:
    # exception()会自动记录异常类型、错误信息和堆栈
    logger.exception("除法运算错误")

运行结果

ERROR:__main__:除法运算错误
Traceback (most recent call last):
  File "/path/to/script.py", line 9, in <module>
    a = 1 / 0
ZeroDivisionError: division by zero

注意exception()方法只能在except块中使用,且默认级别是ERROR

3.5 异步日志处理(高并发场景必须)

同步日志在高并发场景下会导致IO 阻塞,Logging 模块提供了QueueHandlerQueueListener实现异步日志:

import logging
import logging.handlers
import multiprocessing
import threading
import time

# 1. 创建日志队列(用于存储日志消息)
log_queue = multiprocessing.Queue()

# 2. 创建QueueHandler(将日志发送到队列)
queue_handler = logging.handlers.QueueHandler(log_queue)
root_logger = logging.getLogger()
root_logger.addHandler(queue_handler)
root_logger.setLevel(logging.DEBUG)

# 3. 创建QueueListener(从队列读取日志并输出到文件/控制台)
file_handler = logging.FileHandler("async.log", encoding="utf-8")
file_formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
file_handler.setFormatter(file_formatter)

console_handler = logging.StreamHandler()
console_handler.setFormatter(file_formatter)

# 初始化QueueListener:监听队列,将日志输出到多个Handler
listener = logging.handlers.QueueListener(
    log_queue,
    file_handler,
    console_handler,
    respect_handler_level=True  # 尊重Handler的级别设置
)

# 4. 启动Listener(在子线程或子进程中运行)
listener.start()

# 5. 高并发场景模拟:100个线程同时生成日志
def generate_logs():
    for i in range(10):
        logging.info(f"线程{threading.current_thread().name}生成日志{i}")
        time.sleep(0.01)

threads = []
for _ in range(100):
    t = threading.Thread(target=generate_logs)
    threads.append(t)
    t.start()

# 6. 等待所有线程结束
for t in threads:
    t.join()

# 7. 停止Listener
listener.stop()

异步日志的优势

  1. 主线程 / 进程无需等待日志 IO,提高系统并发能力;
  2. 避免多进程写入同一日志文件的竞争问题。

第四章 生产环境实战场景

4.1 Flask/Django Web 应用中的日志配置

在 Web 应用中,需要将请求 ID、IP 地址、请求方法、路径等信息添加到日志中,以下是 Flask 应用的配置示例:

from flask import Flask, g, request
import logging
import logging.config
import uuid
import os

# 1. 自定义请求ID过滤器
class RequestIdFilter(logging.Filter):
    def filter(self, record):
        record.request_id = getattr(g, "request_id", "N/A")
        record.remote_ip = request.remote_addr if request else "N/A"
        record.request_method = request.method if request else "N/A"
        record.request_path = request.path if request else "N/A"
        return True

# 2. Flask应用初始化
app = Flask(__name__)

# 3. 应用请求钩子:生成请求ID
@app.before_request
def before_request():
    g.request_id = uuid.uuid4().hex
    logging.info(f"请求开始:{request.method} {request.path}")

# 4. 应用请求钩子:记录请求结束
@app.after_request
def after_request(response):
    logging.info(f"请求结束:{request.method} {request.path},响应状态:{response.status_code}")
    return response

# 5. 日志配置
LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "web": {
            "format": "%(asctime)s - %(request_id)s - %(remote_ip)s - %(request_method)s %(request_path)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "filters": {
        "request": {"()": lambda: RequestIdFilter()}
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "web",
            "filters": ["request"]
        },
        "file": {
            "class": "logging.handlers.TimedRotatingFileHandler",
            "level": "DEBUG",
            "formatter": "web",
            "filename": "flask_app.log",
            "when": "D",
            "backupCount": 30,
            "encoding": "utf-8"
        }
    },
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["console", "file"]
        },
        "flask": {
            "level": "WARNING",
            "handlers": ["file"]
        }
    }
}

# 6. 加载日志配置
logging.config.dictConfig(LOGGING_CONFIG)

# 7. 测试API
@app.route("/")
def index():
    logging.debug("处理根路径请求")
    return "Hello, Flask!"

if __name__ == "__main__":
    app.run()

访问 API 后的日志

2024-05-20 16:30:00 - xxxxxxx - 127.0.0.1 - GET / - __main__ - INFO - 请求开始:GET /
2024-05-20 16:30:00 - xxxxxxx - 127.0.0.1 - GET / - __main__ - DEBUG - 处理根路径请求
2024-05-20 16:30:00 - xxxxxxx - 127.0.0.1 - GET / - __main__ - INFO - 请求结束:GET /,响应状态:200

4.2 多模块项目的日志配置

在多模块项目中,需要为业务模块、工具模块、第三方模块配置不同的日志级别和输出方式:

# 项目结构
myproject/
├── app/
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
├── lib/
│   ├── __init__.py
│   └── utils.py
└── main.py
# app/__init__.py 中的日志配置
import logging.config

LOGGING_CONFIG = {
    "version": 1,
    "loggers": {
        "app": {  # 业务模块日志:INFO级别,输出到文件
            "level": "INFO",
            "handlers": ["app_file"],
            "propagate": False
        },
        "lib": {  # 工具模块日志:DEBUG级别,输出到文件+控制台
            "level": "DEBUG",
            "handlers": ["lib_file", "console"],
            "propagate": False
        },
        "requests": {  # 第三方模块日志:WARNING级别,屏蔽DEBUG
            "level": "WARNING",
            "handlers": ["thirdparty_file"],
            "propagate": False
        }
    }
    # 省略其他配置(Handler、Formatter)
}

logging.config.dictConfig(LOGGING_CONFIG)
# app/module1.py 中的日志使用
import logging

logger = logging.getLogger(__name__)  # Logger名称为"app.module1",自动继承"app"的配置

def func():
    logger.info("module1的业务日志")

4.3 日志的监控与分析

生产环境的日志需要集中收集、检索、监控,常用的方案是ELK Stack(Elasticsearch+Logstash+Kibana)Loki+Grafana。以下是将 Logging 日志输出到 ELK 的示例:

import logging
import logging.handlers

# 配置SysLogHandler输出到Logstash
logstash_handler = logging.handlers.SysLogHandler(
    address=("logstash-server", 514),
    facility=logging.handlers.SysLogHandler.LOG_LOCAL0
)
logstash_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(message)s"))

logger = logging.getLogger("app")
logger.addHandler(logstash_handler)
logger.setLevel(logging.INFO)

# 生成日志
logger.info("支付订单成功,订单号:123456")

监控告警:可以在 Kibana/Grafana 中配置告警规则,当 ERROR 级别日志数量超过阈值时,自动发送邮件 / 短信告警。


第五章 常见踩坑点与避坑指南

5.1 坑点 1:日志不输出

原因

  1. Logger/Handler 的级别设置过高;
  2. 未配置 Handler 或 Handler 未添加到 Logger;
  3. basicConfig()被覆盖或未生效;
  4. propagate属性设为False且未配置 Handler。避坑
  • 检查 Logger 和 Handler 的级别;
  • 使用logger.handlers查看是否有 Handler;
  • 确保basicConfig()在第一次调用 Logger 前执行。

5.2 坑点 2:重复日志

原因

  1. 子 Logger 的propagate属性为True,且父 Logger 和子 Logger 都配置了 Handler;
  2. 重复调用addHandler()添加同一 Handler。避坑
  • 关闭子 Logger 的propagate属性;
  • 添加 Handler 前检查是否已存在:if handler not in logger.handlers: logger.addHandler(handler)

5.3 坑点 3:日志滚动不生效

原因

  1. maxBytesbackupCount参数设置错误;
  2. TimedRotatingFileHandlerwhen参数与interval参数不匹配;
  3. 日志文件路径不存在或无写入权限。避坑
  • 检查参数是否正确(如maxBytes=100*1024*1024而不是100MB);
  • 确保日志文件路径存在:os.makedirs(os.path.dirname(log_file), exist_ok=True)

5.4 坑点 4:多进程日志混乱

原因

  1. 多个进程同时写入同一日志文件,导致文件内容被覆盖;
  2. 未设置RotatingFileHandlerdelay=True参数。避坑
  • 每个进程使用不同的日志文件名(如包含进程 ID:app_{}.log.format(os.getpid()));
  • 使用QueueHandler+QueueListener实现异步日志;
  • 使用第三方库concurrent-log-handler(线程 / 进程安全的日志 Handler)。

5.5 坑点 5:日志格式占位符错误

原因

  1. 使用了 Logging 模块不支持的占位符(如%(time)s而不是%(asctime)s);
  2. 自定义 Formatter 时未包含%(message)s避坑
  • 参考 Logging 模块文档使用正确的占位符;
  • 确保 Formatter 包含%(message)s(日志内容)。

第六章 Logging 模块最佳实践

6.1 永远不要用print()代替logging

  • print()的输出无法控制级别,会淹没关键日志;
  • print()的输出没有时间、模块、线程等上下文信息;
  • print()的输出在容器 / 生产环境中容易丢失。

6.2 生产环境禁用 DEBUG 日志

  • DEBUG 日志会产生大量冗余信息,占用磁盘空间;
  • DEBUG 日志可能包含敏感信息(如密码、密钥)。

6.3 用dictConfig()做集中配置

  • 避免在代码中硬编码日志配置;
  • 支持多环境配置(开发 / 测试 / 生产);
  • 便于动态修改和维护。

6.4 配置日志滚动和过期清理

  • 使用RotatingFileHandlerTimedRotatingFileHandler
  • 定期清理旧日志(如保留 30 天),避免磁盘空间耗尽。

6.5 为不同模块设置不同日志级别

  • 业务模块:INFO 级别;
  • 工具模块:DEBUG 级别;
  • 第三方模块:WARNING 级别(屏蔽无关日志)。

6.6 添加上下文信息(如请求 ID)

  • 在 Web 应用中添加请求 ID,便于追踪完整请求链路;
  • 在异步任务中添加任务 ID,便于定位任务执行情况。

6.7 异步日志处理(高并发场景)

  • 使用QueueHandler+QueueListener实现异步日志;
  • 避免同步日志的 IO 阻塞。

6.8 监控日志并配置告警

  • 集中收集日志到 ELK/Loki;
  • 配置 ERROR 级别日志的告警规则;
  • 定期分析日志,发现潜在问题。

6.9 测试日志配置

  • 在开发环境测试日志的输出格式和级别;
  • 测试日志滚动和过期清理功能;
  • 测试异常日志的堆栈输出。

博客结语

Python Logging 模块看似简单,但要配置好生产环境的日志系统,需要理解其核心组件、配置方式、进阶特性最佳实践。本文通过 15000 字 + 的内容,覆盖了从入门到生产的所有知识点,包括真实踩坑案例、代码示例、避坑指南。希望读者能将这些经验应用到实际开发中,构建稳定、可维护、可监控的日志系统,避免因日志问题导致的线上故障排查困难。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值