当前位置: 代码网 > it编程>前端脚本>Python > Python日志模块logging的新手入门教程

Python日志模块logging的新手入门教程

2026年09月22日 Python 我要评论
一、为什么不能用 print 代替日志?很多新手刚学 python 时,调试代码全靠 print():print("用户登录成功")print("数据库连接失败")print("订单处理完成")代码量

一、为什么不能用 print 代替日志?

很多新手刚学 python 时,调试代码全靠 print()

print("用户登录成功")
print("数据库连接失败")
print("订单处理完成")

代码量少时没问题,但项目一复杂,print 的缺陷就暴露了:

问题printlogging
无法分级所有信息混在一起五级日志,按需过滤
无法持久化程序结束就没了自动写入文件保存
无法控制输出只能打印到控制台控制台/文件/邮件/网络随意切换
性能差无条件执行字符串拼接级别不够时直接跳过,零开销
无上下文纯字符串,不知道从哪来的时间/模块/行号/级别全记录
多线程不安全输出可能交错混乱内置锁机制,线程安全

一句话总结print 是往屏幕上吐字符串,logging 是一条完整的"日志生产线"——从产生日志到最终落地,每一步都可控。

二、logging 模块的四大核心组件

python 的 logging 模块采用分层解耦架构,核心由四大组件构成:

你的代码:logger.info("...")
       │
       ▼
┌───────────────┐  ① logger:日志入口,级别过滤的第一道关卡
│    logger     │  getlogger('app.api'),命名空间支持层级继承
└───────┬───────┘
        │
        ▼
┌───────────────┐  ② filter:比级别更细的过滤,也可给 record 注入字段
│    filter     │  addfilter()
└───────┬───────┘
        │
        ▼
┌───────────────┐  ③ handler:决定日志"去哪",级别过滤的第二道关卡
│    handler    │  stream / file / rotating / smtp / socket / queue
└───────┬───────┘
        │
        ▼
┌───────────────┐  ④ formatter:决定日志"长什么样"
│   formatter   │  %(asctime)s %(name)s %(levelname)s %(message)s
└───────┬───────┘
        │
        ▼
  控制台 / 文件 / 网络 / 邮件 / 消息队列

1. logger(日志器)—— 入口和控制中心

logger 是你直接打交道的对象,主要职责包括:

  • 接收日志调用:提供 debug()info()warning()error()critical() 等方法
  • 级别过滤:判断日志是否达到设定阈值,决定是否继续处理
  • 层级管理:支持以点号分隔的命名空间(如 app.db.query),子 logger 自动继承父 logger 配置
  • 分发日志:将日志消息传递给绑定的 handler 进行输出
import logging

# 推荐用 __name__ 作为名称,自动形成层级结构
logger = logging.getlogger(__name__)
logger.setlevel(logging.debug)  # 设置最低日志级别

2. handler(处理器)—— 决定日志去哪

handler 决定日志的输出目标,一个 logger 可以绑定多个 handler:

handler用途
streamhandler输出到控制台
filehandler输出到文件
rotatingfilehandler按文件大小轮转,防止日志撑爆磁盘
timedrotatingfilehandler按时间轮转(每天/每小时)
smtphandler发送邮件通知
httphandler发送到远程服务器
queuehandler异步队列处理,适合高并发场景
nullhandler库开发用,避免未配置时输出到 stderr

3. formatter(格式化器)—— 决定日志长什么样

formatter 定义日志的输出格式,通过占位符配置:

占位符含义
%(asctime)s日志产生时间
%(name)s日志器名称
%(levelname)s日志级别名称
%(message)s日志内容
%(filename)s产生日志的文件名
%(lineno)d产生日志的代码行号
%(funcname)s产生日志的函数名

4. filter(过滤器)—— 精细化筛选

filter 用于比级别更细粒度的过滤,可基于名称、自定义条件等进行筛选,通过 addfilter() 挂载到 logger 或 handler 上。

三、五种标准日志级别

logger 定义了五个标准级别,数值越大越严重:

级别数值用途开发环境生产环境
debug10调试细节,记录变量值、执行流程开启关闭
info20正常流程,确认程序按预期运行开启开启
warning30潜在问题,但不影响运行开启开启
error40发生错误,功能受影响开启开启
critical50严重故障,可能导致程序崩溃开启开启

默认级别为 warning,只有 ≥ 设定级别的日志才会被处理。

四、logger 的内部执行流程

当调用 logger.info("msg") 时,内部流程如下:

调用 logger 方法
    ↓
创建 logrecord 对象
    ↓
logger 级别过滤(是否 ≥ 设定级别?)
    ↓
filter 过滤(自定义筛选条件)
    ↓
分发至所有绑定的 handler
    ↓
handler 级别过滤 → formatter 格式化 → emit() 输出

关键规则:日志级别需同时 ≥ logger 级别和 handler 级别时才会输出。

五、从入门到进阶:三种配置方式

方式一:基础配置(适合简单脚本)

使用 logging.basicconfig() 一行搞定:

import logging

logging.basicconfig(
    level=logging.debug,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    datefmt="%y-%m-%d %h:%m:%s",
    filename="app.log",       # 输出到文件
    filemode="a",             # 追加模式
    encoding="utf-8"          # 避免中文乱码
)

logger = logging.getlogger(__name__)
logger.info("服务启动成功")

basicconfig() 仅在首次调用时生效,第二次调用无效。

方式二:手动组装(适合正式项目)

显式创建 logger、handler 和 formatter,灵活组合:

import logging

def setup_logger():
    # 1. 创建 logger
    logger = logging.getlogger(__name__)
    logger.setlevel(logging.debug)
    logger.propagate = false  # 禁止向上传播,避免重复输出

    # 2. 创建控制台 handler
    console_handler = logging.streamhandler()
    console_handler.setlevel(logging.info)  # 控制台只输出 info 及以上

    # 3. 创建文件 handler
    file_handler = logging.filehandler("app.log", encoding="utf-8")
    file_handler.setlevel(logging.debug)  # 文件记录所有级别

    # 4. 创建 formatter
    formatter = logging.formatter(
        "%(asctime)s | %(levelname)-8s | %(name)s | %(filename)s:%(lineno)d | %(message)s",
        datefmt="%y-%m-%d %h:%m:%s"
    )

    # 5. 绑定 formatter 到 handler
    console_handler.setformatter(formatter)
    file_handler.setformatter(formatter)

    # 6. 绑定 handler 到 logger
    logger.addhandler(console_handler)
    logger.addhandler(file_handler)

    return logger

# 使用
logger = setup_logger()
logger.debug("调试信息")
logger.info("正常信息")
logger.error("错误信息", exc_info=true)  # 记录完整异常堆栈

方式三:字典配置(适合大型项目)

通过 logging.config.dictconfig() 加载外部配置,支持 json/yaml 格式:

import logging.config

config = {
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "standard": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.streamhandler",
            "level": "debug",
            "formatter": "standard",
            "stream": "ext://sys.stdout"
        },
        "file": {
            "class": "logging.filehandler",
            "level": "info",
            "formatter": "standard",
            "filename": "app.log",
            "encoding": "utf8"
        }
    },
    "loggers": {
        "my_app": {
            "level": "debug",
            "handlers": ["console", "file"],
            "propagate": false
        }
    }
}

logging.config.dictconfig(config)
logger = logging.getlogger("my_app")

六、常用方法速查

方法说明
logger.debug(msg)记录 debug 级别日志
logger.info(msg)记录 info 级别日志
logger.warning(msg)记录 warning 级别日志
logger.error(msg)记录 error 级别日志
logger.critical(msg)记录 critical 级别日志
logger.exception(msg)记录 error 级别 + 完整异常堆栈(等价于 error(msg, exc_info=true)
logger.log(level, msg)动态指定日志级别

记录变量的正确写法(使用 %s 占位符,而非 f-string):

#  推荐:延迟格式化,性能更好
logger.info("用户 %s 登录成功,id: %d", username, user_id)

#  不推荐:f-string 会无条件计算
logger.info(f"用户 {username} 登录成功,id: {user_id}")

七、日志轮转:避免日志文件撑爆磁盘

当程序长期运行时,单一日志文件会越来越大。logging.handlers 提供了两种轮转机制:

按文件大小轮转(rotatingfilehandler)

from logging.handlers import rotatingfilehandler

handler = rotatingfilehandler(
    "app.log",
    maxbytes=10 * 1024 * 1024,  # 单个文件最大 10mb
    backupcount=5,               # 保留最近 5 个历史文件
    encoding="utf-8"
)

按时间轮转(timedrotatingfilehandler)

from logging.handlers import timedrotatingfilehandler

handler = timedrotatingfilehandler(
    "app.log",
    when="d",       # 轮转单位:s(秒)、m(分)、h(时)、d(天)、midnight(午夜)
    interval=1,     # 间隔时间
    backupcount=7,  # 保留最近 7 天日志
    encoding="utf-8"
)

八、新手常见坑点

坑点原因解决方案
debug 日志不输出默认级别为 warning手动调用 logger.setlevel(logging.debug)
日志重复打印多次 addhandler 或 propagate 未关闭调用 logger.handlers.clear() 或设置 propagate=false
日志文件乱码未指定编码filehandler 中指定 encoding="utf-8"
设置级别无效只给 logger 设了级别,没给 handler 设logger 和 handler 都要设置级别
f-string 性能差即使级别不够也会执行字符串拼接改用 %s 占位符

九、最佳实践总结

__name__ 命名 logger:便于追踪日志来源,自动形成层级结构

避免重复配置:不要在每个模块调用 basicconfig(),应在应用入口集中配置

库开发加 nullhandler:避免未配置时日志输出到 stderr

生产环境慎用 debug:高频路径输出 debug 日志会影响性能

使用 exc_info=trueexception():记录完整异常堆栈,是排障的关键信息

%s 占位符而非 f-string:避免不必要的性能开销

设置 propagate=false:避免日志被传递到 root logger,导致重复输出

十、完整生产级示例

import logging
import logging.handlers
import json
from pathlib import path


class jsonformatter(logging.formatter):
    """json 格式化器,便于 elk/loki 采集分析"""
    def format(self, record):
        log_data = {
            "time": self.formattime(record),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getmessage(),
            "module": record.module,
            "line": record.lineno,
        }
        if record.exc_info:
            log_data["exception"] = self.formatexception(record.exc_info)
        return json.dumps(log_data, ensure_ascii=false)


def setup_production_logging(log_dir="logs"):
    """配置生产级日志体系"""
    log_path = path(log_dir)
    log_path.mkdir(exist_ok=true)

    root_logger = logging.getlogger()
    root_logger.setlevel(logging.debug)

    # 统一格式
    fmt = logging.formatter(
        "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
        datefmt="%y-%m-%d %h:%m:%s"
    )

    # 控制台 handler:只输出 warning 及以上
    console = logging.streamhandler()
    console.setlevel(logging.warning)
    console.setformatter(fmt)

    # 文件 handler:输出所有级别,按大小轮转
    file_handler = logging.handlers.rotatingfilehandler(
        log_path / "app.log",
        maxbytes=10 * 1024 * 1024,  # 10mb 轮转
        backupcount=5,
        encoding="utf-8",
    )
    file_handler.setlevel(logging.debug)
    file_handler.setformatter(fmt)

    # 错误专用 handler
    error_handler = logging.handlers.rotatingfilehandler(
        log_path / "error.log",
        maxbytes=5 * 1024 * 1024,
        backupcount=3,
        encoding="utf-8",
    )
    error_handler.setlevel(logging.error)
    error_handler.setformatter(fmt)

    root_logger.addhandler(console)
    root_logger.addhandler(file_handler)
    root_logger.addhandler(error_handler)


# 使用
setup_production_logging()
logger = logging.getlogger(__name__)

logger.info("应用启动")
logger.error("请求失败", exc_info=true)

核心思想:logger 负责"记录什么",handler 负责"输出到哪",formatter 负责"长什么样"。这种解耦设计让日志系统既灵活又可扩展。掌握这套体系,你就从"用 print 调试"的新手,迈向了"用 logging 构建可观测系统"的工程师。

以上就是python日志模块logging的新手入门教程的详细内容,更多关于python logging模块的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com