19. python logging
19. python logging
19.1 logging模块的日志级别
- 日志级别:
- 日志级别用于区分日志的严重程度。以下是一些常见的日志级别(按严重程度递增):
DEBUG
:用于记录调试信息,通常不需要在生产环境中启用。INFO
:用于记录程序的一般信息或状态。WARNING
:用于记录潜在的问题或错误。ERROR
:用于记录错误或异常。CRITICAL
:用于记录严重的错误或异常。
- 默认情况下,
logging
模块的日志级别被设置为WARNING
。这意味着只有级别为WARNING
或更高级别的日志会被记录。
- 日志级别用于区分日志的严重程度。以下是一些常见的日志级别(按严重程度递增):
19.2 logging模块级别函数
logging.basicConfig()
logging.basicConfig()
适用于简单的日志记录需求,例如:
- 快速记录日志到 标准输出 或 文件
- 控制 日志级别(DEBUG、INFO、WARNING等)
- 指定 基本的日志格式
- 适用于小型脚本或一次性执行的任务
logging.basicConfig()函数常用参数
logging.basicConfig()
函数的关键字参数如下:
参数名 | 作用 |
---|---|
level |
设置日志级别(如 logging.DEBUG 、logging.INFO 等)。 |
filename |
指定日志输出到文件的文件名。 |
filemode |
设置日志文件的模式(如 'w' 覆盖,'a' 追加)。 |
format |
指定日志格式化字符串,如 '%(asctime)s - %(levelname)s - %(message)s' 。 |
datefmt |
指定日期时间格式,例如 '%Y-%m-%d %H:%M:%S' 。 |
handlers |
指定日志处理器列表,替代 filename 和 stream 。 |
stream |
指定日志输出的流,如 sys.stdout 或 sys.stderr (与 filename 互斥)。 |
encoding |
指定日志文件的编码,仅在 filename 存在时生效(如 'utf-8' )。 |
format
参数用于指定日志消息的格式,它可以包含多个格式化占位符,以下是常用的格式说明符:
格式符 | 含义 |
---|---|
%(asctime)s |
当前时间,默认格式 YYYY-MM-DD HH:MM:SS,sss (毫秒) |
%(levelname)s |
日志级别名称(如 DEBUG 、INFO 、WARNING 等) |
%(name)s |
记录器的名称(即 logger 对象的 name 属性) |
%(message)s |
实际的日志消息 |
%(pathname)s |
日志记录发生的源文件的完整路径 |
%(filename)s |
日志记录发生的文件名(不含路径) |
%(module)s |
日志记录所在的模块名(filename 去掉扩展名) |
%(funcName)s |
日志记录所在的函数名 |
%(lineno)d |
日志记录发生的行号 |
%(thread)d |
线程 ID |
%(threadName)s |
线程名称 |
%(process)d |
进程 ID |
%(processName)s |
进程名称 |
%(msecs)d |
当前时间的毫秒部分 |
%(relativeCreated)d |
相对程序启动时间(毫秒) |
%(levelno)d |
日志级别的数值(如 DEBUG=10 ,INFO=20 ) |
简单示例:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(module)s - %(lineno)d',
datefmt='%Y/%m/%d %H:%M:%S',
filename='app.log',
filemode='w',
encoding='utf-8'
)
logging.info("这是一个日志消息")
此代码会将日志消息写入 app.log
文件,采用 utf-8
编码,格式包含时间、级别和消息内容。
推荐的替代方案:使用 logging 进行更灵活的配置
对于运维工程师,推荐使用自定义日志配置,实现:
- 同时输出到 文件 和 控制台
- 不同级别 记录到 不同文件
- 日志切割
- 更详细的日志格式
19.3 logging模块的类(自定义日志配置)
日志库采用模块化方法,并提供几类组件:记录器、处理器、过滤器和格式化器。
-
日志记录器(Logger):
- 日志记录器是日志系统的入口点。可以通过
logging.getLogger(name)
方法创建日志记录器。如果未提供名称,则会返回默认记录器。 getLogger()
返回对具有指定名称的记录器实例的引用(如果已提供),或者如果没有则返回root
。名称是以句点分隔的层次结构。多次调用getLogger()
具有相同的名称将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。
记录器对象上使用最广泛的方法分为两类:配置和消息发送。
这些是最常见的配置方法:
Logger.setLevel()
指定记录器将处理的最低严重性日志消息,其中 debug 是最低内置严重性级别, critical 是最高内置严重性级别。 例如,如果严重性级别为 INFO ,则记录器将仅处理 INFO 、 WARNING 、 ERROR 和 CRITICAL 消息,并将忽略 DEBUG 消息。Logger.addHandler()
和Logger.removeHandler()
从记录器对象中添加和删除处理器对象。Logger.addFilter()
和Logger.removeFilter()
可以添加或移除记录器对象中的过滤器。
- 日志记录器是日志系统的入口点。可以通过
-
日志处理器(Handler):
- 处理器决定了日志的输出目标(如控制台、文件、网络等)。常用的处理器包括:
StreamHandler
:将日志发送到流(如sys.stdout
或sys.stderr
)。FileHandler
:将日志写入文件。RotatingFileHandler
:将日志写入文件,并在文件大小超过指定值时自动切割(支持日志回滚)。TimedRotatingFileHandler
:根据时间间隔切割日志文件(支持日志回滚)。
Handler
对象负责将适当的日志消息(基于日志消息的严重性)分派给处理器的指定目标。Logger
对象可以使用addHandler()
方法向自己添加零个或多个处理器对象。作为示例场景,应用程序可能希望将所有日志消息发送到日志文件,将错误或更高的所有日志消息发送到标准输出,以及将所有关键消息发送至一个邮件地址。 此方案需要三个单独的处理器,其中每个处理器负责将特定严重性的消息发送到特定位置。- 处理器常用方法:
setLevel()
方法,就像在日志记录器对象中一样,指定将被分派到适当目标的最低严重性。 为什么有两个setLevel()
方法? 在日志记录器中设置的级别确定要传递给其处理器的消息的严重性。 每个处理器中设置的级别则确定该处理器将发送哪些消息。setFormatter()
选择一个该处理器使用的 Formatter 对象。addFilter()
和removeFilter()
分别在处理器上配置和取消配置过滤器对象。
- 处理器决定了日志的输出目标(如控制台、文件、网络等)。常用的处理器包括:
-
日志过滤器(Filter):
- 日志记录可能不需要全部保存,如只保存某个级别的日志,或只保存包含某个关键子的日志等,这时可以使用过滤器限定要保存的部分。过滤器是由Filter类实例化的对象。记录器和处理器都可以使用过滤器来设置比日志级别更细粒度、更复杂的相关过滤功能。
-
日志格式化器(Formatter):
- 格式器用于指定日志的输出格式。通过
logging.Formatter
创建格式器,并可以设置时间格式、日志级别、日志消息等内容。 - fmt指定消息格式化字符串,datefmt指定日期格式化字符串,style设置样式指示符。
- 如果
style
为'%'
,则消息格式字符串将使用%(<dictionary key>)s
样式的字符串替换; - 如果样式为
'{'
,则将假定消息格式字符串与str.format()
兼容(使用关键字参数);
- 如果
- 格式器用于指定日志的输出格式。通过
示例:将日志消息同时输出到屏幕和文件
#!/usr/bin/python3
#_*_coding:utf-8_*_
import logging
#创建记录器,并其命名为Test
logger = logging.getLogger('Test')
#默认日志级别为WARNING,这里改为DEBUG
logger.setLevel(logging.DEBUG)
#创建FileHandler处理器,将日志消息输出到文件中,并设置特定的消息日志和日志格式
logfile = logging.FileHandler(filename='mylog.log',mode='w')
formatter1 = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%a, %b %Y %H:%M:%S')
logfile.setFormatter(formatter1)
#创建StreamHandler处理器,将WARNING或更高级别的日志消息输出到控制台,设置特定消息格式
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
formatter2 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console.setFormatter(formatter2)
#将处理器添加到记录器
logger.addHandler(logfile)
logger.addHandler(console)
#测试,输出不同的日志消息
logger.debug('这是debug')
logger.info('这是info')
logger.warning('这是warning')
logger.error('这是error')
logger.critical('这是critical')
示例:使用日志文件记录异常处理信息
#!/usr/bin/python3
#_*_coding:utf-8_*_
import logging
#创建记录器
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#创建格式化器
logfile = logging.FileHandler(filename='mylog.log',mode='w')
formatter = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%a, %b %Y %H:%M:%S')
logfile.setFormatter(formatter)
#将处理器添加到记录器
logger.addHandler(logfile)
#测试异常处理函数
def func(num1,num2):
try:
x = num1 * num2
y = num1 / num2
return x,y
except Exception:
#输出日志消息,exc_info参数确定是否将异常信息添加到日志消息中
logger.error('出现异常!',exc_info=True)
logger.info('已记录日志!')
if __name__ == '__main__':
func(2,0) #除以0触发异常
——>>输出:
(test-venv) root@jiaxing:~/test-venv# cat mylog.log
Sun, Mar 2025 00:35:23 test.py[line:24] ERROR 出现异常!
Traceback (most recent call last):
File "/root/test-venv/test.py", line 20, in func
y = num1 / num2
ZeroDivisionError: division by zero
Sun, Mar 2025 00:35:23 test.py[line:25] INFO 已记录日志!
示例:按日志文件大小切割(RotatingFileHandler
)
import logging
from logging.handlers import RotatingFileHandler
# 配置日志
logger = logging.getLogger("MyLogger")
logger.setLevel(logging.DEBUG)
# 创建一个 RotatingFileHandler,最大 5MB,每次超出后最多保留 3 个备份
handler = RotatingFileHandler("app.log", maxBytes=5*1024*1024, backupCount=3, encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
# 添加处理器到 logger
logger.addHandler(handler)
# 测试写入日志
for i in range(10000):
logger.info(f"这是第 {i} 条日志")
参数解析:
maxBytes=5*1024*1024
:日志文件大小达到 5MB 后切割backupCount=3
:最多保留 3 个历史日志文件(app.log.1
,app.log.2
,app.log.3
)encoding="utf-8"
:支持中文日志
示例:按时间切割日志文件(TimedRotatingFileHandler
)
import logging
from logging.handlers import TimedRotatingFileHandler
# 配置日志
logger = logging.getLogger("MyTimedLogger")
logger.setLevel(logging.DEBUG)
# 创建一个 TimedRotatingFileHandler,每天切割一次日志,最多保留 7 天
handler = TimedRotatingFileHandler("timed_app.log", when="D", interval=1, backupCount=7, encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
# 添加处理器到 logger
logger.addHandler(handler)
# 测试写入日志
for i in range(100):
logger.info(f"这是第 {i} 条定时日志")
参数解析:
when="D"
:按天切割(支持"S"
秒,"M"
分,"H"
小时,"D"
天,"W0-W6"
每周几)interval=1
:间隔 1 天切割一次backupCount=7
:最多保留 7 个历史日志文件encoding="utf-8"
:支持中文日志
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通