主页
文章
分类
标签
关于
Python日志系统-logging
发布于: 2025-4-4   更新于: 2025-7-15   收录于: Python , python库
文章字数: 3771   阅读时间: 8 分钟   阅读量:

前言

本文将记录如果使用python的loggin日志以及封装一个即开即用的log模块 想要复制模块的点击此处一键跳转到

Python 标准库 logging 采用「日志器(Logger) - 处理器(Handler) - 格式化器(Formatter)」三层架构,原生使用就是直接操作这三层组件,无需封装类/方法。

基础用法(一行输出到控制台)

这是最基础的用法,直接使用 logging 模块的全局函数,自动创建默认日志器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import logging

# 基础配置:设置日志级别 + 输出格式(可选)
logging.basicConfig(
    level=logging.INFO,  # 只输出 ≥INFO 级别的日志
    format="%(asctime)s - %(levelname)s - %(message)s",  # 日志格式
    datefmt="%Y-%m-%d %H:%M:%S"  # 时间格式
)

# 直接使用全局日志器输出不同级别日志
logging.debug("调试信息(不会输出,因为级别是INFO)")
logging.info("普通业务日志(会输出)")
logging.warning("警告信息")
logging.error("错误信息")
logging.critical("严重错误")

输出结果

1
2
3
4
2025-12-26 15:30:00 - INFO - 普通业务日志(会输出)
2025-12-26 15:30:00 - WARNING - 警告信息
2025-12-26 15:30:00 - ERROR - 错误信息
2025-12-26 15:30:00 - CRITICAL - 严重错误

关键说明:

  • logging.basicConfig():是「一次性配置」,仅在第一次调用时生效(后续调用会被忽略);
  • 日志级别优先级:DEBUG < INFO < WARNING < ERROR < CRITICAL,设置 level=logging.INFO 会过滤 DEBUG 日志;
  • 不调用 basicConfig() 时,默认只输出 ≥WARNING 级别的日志到控制台,且格式极简。

进阶用法(控制台+文件双输出)

原生使用的核心是手动创建「日志器(Logger)」+「处理器(Handler)」+「格式化器(Formatter)」,实现更灵活的配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import logging
from logging.handlers import RotatingFileHandler

# ===================== 1. 创建日志器(核心) =====================
logger = logging.getLogger("my_app")  # 命名日志器(区分模块)
logger.setLevel(logging.DEBUG)  # 日志器总级别(所有处理器都受此限制)
logger.propagate = False  # 禁用传播(避免重复输出)

# ===================== 2. 创建格式化器(日志格式) =====================
formatter = logging.Formatter(
    fmt="%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)

# ===================== 3. 创建控制台处理器 =====================
console_handler = logging.StreamHandler()  # 输出到控制台
console_handler.setLevel(logging.INFO)  # 控制台只输出 ≥INFO 日志
console_handler.setFormatter(formatter)  # 绑定格式
logger.addHandler(console_handler)  # 添加到日志器

# ===================== 4. 创建文件处理器(滚动日志) =====================
file_handler = RotatingFileHandler(
    filename="logs/app.log",  # 日志文件路径
    maxBytes=10*1024*1024,    # 单个文件10MB
    backupCount=5,            # 保留5个备份文件
    encoding="utf-8"          # 中文兼容
)
file_handler.setLevel(logging.DEBUG)  # 文件输出 ≥DEBUG 日志(更详细)
file_handler.setFormatter(formatter) # 绑定格式
logger.addHandler(file_handler) # 添加到日志器

# ===================== 5. 原生使用日志器 =====================
logger.debug("调试信息(只写入文件,不输出到控制台)")
logger.info("普通信息(控制台+文件都输出)")
logger.warning("警告信息")

# 记录异常(带完整栈信息)
try:
    1 / 0
except Exception as e:
    # exc_info=True:打印异常栈;也可以直接传 exc_info=e
    logger.error("除法运算异常", exc_info=True)

核心组件解析:

组件 作用 常用实现类
Logger(日志器) 日志入口,负责收集日志并分发给处理器 logging.getLogger(name)
Handler(处理器) 决定日志输出位置(控制台/文件/网络) StreamHandler(控制台)、FileHandler(文件)、RotatingFileHandler(滚动文件)、TimedRotatingFileHandler(按时间切分)
Formatter(格式化器) 定义日志的输出格式 logging.Formatter(fmt, datefmt)
Filter(过滤器) 精细化过滤日志(可选) logging.Filter

常用场景

按日期切分日志文件

使用 TimedRotatingFileHandler 替代 RotatingFileHandler,实现按时间轮转:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from logging.handlers import TimedRotatingFileHandler

# 按天切分,保留30天日志,每天0点新建文件
file_handler = TimedRotatingFileHandler(
    filename="logs/app.log",
    when="D",        # 轮转单位:S(秒)/M(分)/H(时)/D(天)/W0(周一)/midnight(午夜)
    interval=1,      # 每隔1个when单位轮转
    backupCount=30,  # 保留30个文件
    encoding="utf-8"
)

不同模块使用不同日志器

原生使用时,通过「命名日志器」区分模块,便于定位问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# module_llm.py
import logging
# 命名为 "llm",日志中会显示该名称
llm_logger = logging.getLogger("llm")
llm_logger.info("调用大模型接口")

# module_api.py
import logging
# 命名为 "api"
api_logger = logging.getLogger("api")
api_logger.info("接收用户请求")

输出日志到多个文件

给一个日志器添加多个文件处理器,实现日志分类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
logger = logging.getLogger("my_app")

# 普通日志文件
normal_handler = RotatingFileHandler("logs/normal.log", encoding="utf-8")
normal_handler.setLevel(logging.INFO)
logger.addHandler(normal_handler)

# 错误日志单独文件
error_handler = RotatingFileHandler("logs/error.log", encoding="utf-8")
error_handler.setLevel(logging.ERROR)  # 只记录错误日志
logger.addHandler(error_handler)

# 普通日志只进 normal.log,错误日志同时进 normal.log + error.log
logger.info("普通信息")
logger.error("错误信息")

封装模块

此处将模块放到比如说core/logger.py下面:

模块代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# core/logger.py
import logging
import sys
from datetime import datetime
from logging.handlers import RotatingFileHandler
from pathlib import Path

# 日志目录
LOG_DIR = Path("logs") # 定义日志文件存储目录为项目根目录下的 logs 文件夹
LOG_DIR.mkdir(exist_ok=True) # 创建日志目录,exist_ok=True 表示目录已存在时不报错(避免重复创建异常)

# 判断是否为开发环境(根据 stdout 是否为终端)
IS_DEV = sys.stdout.isatty() # 检查标准输出(stdout)是否连接到终端(比如本地运行 python app.py 时返回 True,部署到服务器 / 容器时通常返回 False);


class ColorFormatter(logging.Formatter):
    """
    彩色日志格式化器: 将开发环境彩色日志格式化。
    仅在开发环境(IS_DEV=True)生效,生产环境使用默认格式化器
    """
    COLORS = {
        "DEBUG": "\033[36m",    # Cyan 青色
        "INFO": "\033[32m",     # Green 绿色
        "WARNING": "\033[33m",  # Yellow 黄色
        "ERROR": "\033[31m",    # Red 红色
        "CRITICAL": "\033[35m", # Magenta 洋红
    }
    RESET = "\033[0m" # 重置颜色

    def format(self, record):
        log_color = self.COLORS.get(record.levelname, self.RESET) # 根据日志级别(record.levelname,如 DEBUG/INFO)获取对应颜色
        record.levelname = f"{log_color}{record.levelname}{self.RESET}" # 改写 record.levelname,给级别名称包裹颜色转义码
        return super().format(record) # 调用父类 format 方法完成最终日志格式化(保留原有格式,仅修改级别颜色)


class LoggerManager:
    """日志管理器:按需创建带日期命名的日志器"""
    _loggers = {} # 缓存已创建的日志器,key=日志器名称,value=Logger实例

    @staticmethod # 静态方法:无需实例化类,直接调用 LoggerManager.get_logger()
    def get_logger(
        name: str = "app",
        level: int = logging.INFO,
        max_bytes: int = 10 * 1024 * 1024,  # 10 MB
        backup_count: int = 5,
        console_output: bool = True,
        file_output: bool = True
    ) -> logging.Logger:
        """
        获取配置好的日志器
        
        Args:
            name: 日志器名称(用于区分模块,如 "llm"、"api"),会显示在日志内容中
            level: 日志级别(DEBUG < INFO < WARNING < ERROR < CRITICAL),低于该级别的日志会被过滤
            max_bytes: 	单个日志文件的最大大小(字节),达到后自动切分(滚动日志)
            backup_count: 保留的旧日志文件数量(如 5 表示最多保留 5 个切分后的文件)
            console_output: 是否输出到控制台
            file_output: 是否输出到文件
        """

        # 缓存检查:如果日志器已创建,直接返回缓存实例
        if name in LoggerManager._loggers:
            return LoggerManager._loggers[name]

        # 创建日志器实例
        logger = logging.getLogger(name)
        logger.setLevel(level) # 设置日志器总级别(所有处理器都受此限制)
        logger.propagate = False  # 禁用日志传播:避免日志向上传递到根日志器,导致重复输出

        # 清理已有 handler(避免重复添加,比如重复调用 get_logger 时,例如多次调用 get_logger("api") 时,不会重复添加控制台 / 文件处理器)
        logger.handlers.clear()

        # 日志格式 日志时间+日志器名称+日志级别+日志所在文件:行号+日志内容
        log_format = "%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
        date_format = "%Y-%m-%d %H:%M:%S"

        # 控制台处理器(开发环境彩色)
        if console_output:
            console_handler = logging.StreamHandler() # # 输出到标准输出(控制台)
            console_handler.setLevel(level)  # # 处理器级别(可单独设置,通常和日志器一致)
            if IS_DEV:
                # 开发环境:使用彩色格式化器
                formatter = ColorFormatter(fmt=log_format, datefmt=date_format)
            else:
                # 生产环境:使用默认格式化器(无颜色)
                formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
            console_handler.setFormatter(formatter)
            logger.addHandler(console_handler)

        # 文件处理器(按日期命名 + 滚动备份)
        if file_output:
            today = datetime.now().strftime("%Y-%m-%d")  # 当天日期,如 2025-10-01
            log_file = LOG_DIR / f"{today}.{name}.log" # 日志文件路径:logs/2025-10-01.{日志器名称}.log

            # 滚动日志:使用 RotatingFileHandler,单个文件达到 max_bytes(10MB)时自动切分
            # 切分规则:2025-12-26.log → 2025-12-26.log.1 → 2025-12-26.log.2… 最多保留 backup_count=5 个文件
            file_handler = RotatingFileHandler(
                filename=log_file,
                maxBytes=max_bytes,
                backupCount=backup_count,
                encoding="utf-8"
            )
            file_handler.setLevel(level)
            formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
            file_handler.setFormatter(formatter)
            logger.addHandler(file_handler)

        # 缓存并返回日志器,下次如果遇到同名日志器,直接从缓存中获取,避免重复配置
        LoggerManager._loggers[name] = logger
        return logger


# 全局默认日志器,开箱即用的全局日志器,无需手动调用 get_logger 即可使用;
# 开发环境(IS_DEV=True):日志级别为 DEBUG,显示所有级别日志(便于调试);
# 生产环境(IS_DEV=False):日志级别为 INFO,过滤 DEBUG 日志(减少日志量,提升性能);
logger = LoggerManager.get_logger(
    name="esagent",
    level=logging.DEBUG if IS_DEV else logging.INFO
)

使用方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 使用方式:from core.logger import logger; logger.info("xxx")

# 1. 使用全局默认日志器
from core.logger import logger

logger.debug("开发环境调试信息")
logger.info("普通业务日志")
logger.warning("非致命警告")
logger.error("功能异常")
logger.critical("严重错误,程序可能崩溃")

# 2. 使用模块专属日志器
llm_logger = LoggerManager.get_logger(name="llm", level=logging.DEBUG)
llm_logger.info("调用大模型接口,参数:xxx")

# 3. 记录异常(带栈信息)
try:
    1 / 0
except Exception as e:
    logger.error("除法运算异常", exc_info=e)  # exc_info=True 打印完整栈信息