前言
本文将记录如果使用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 打印完整栈信息
|