python-logging
一、logging简介
Python 的 logging 模块是一个强大的内置日志记录系统,它提供了灵活的日志记录功能,远超简单的 print 语句。这个模块为应用与库实现了灵活的事件日志系统的函数与类。使用标准库提供的 logging API 最主要的好处是,所有的 Python 模块都可能参与日志输出,包括你自己的日志消息和第三方模块的日志消息。
官方文档参见:https://docs.python.org/3.11/library/logging.html#logging.basicConfig
二、logging和print的区别
logging 模块提供了 print 无法比拟的优势:
-
日志级别(Levels):支持 DEBUG, INFO, WARNING, ERROR, CRITICAL 等级别,可以方便地控制不同环境(开发、测试、生产)输出的信息量。
-
灵活的输出目标(Handlers):日志可以同时输出到控制台、文件、网络、邮件、数据库等,而 print 只能输出到标准输出。
-
丰富的格式化(Formatters):可以自定义日志的输出格式,包含时间、模块名、行号、日志级别等信息。
-
性能:在生产环境中,可以将日志级别设为 WARNING,此时 debug() 调用几乎不消耗性能(因为消息不会被格式化),而 print 语句即使注释掉也需要手动操作。
-
结构化与可维护性:logging 提供了清晰的架构,易于配置和管理,适合大型项目。
三、logging基础用法
3.1、日志级别
logging的日志有6个级别,日志的数值越高,日志的级别也就越高。
| 级别 | 数值 | 描述 | 举例 | |
|---|---|---|---|---|
| 1 | NOTEST | - | 无 | |
| 2 | DEBUG | 10 | 调试 | 函数调用、变量值 |
| 3 | INFO | 20 | 一般信息 | 程序入参、执行结果 |
| 4 | WARNING(默认) | 30 | 警告 | 磁盘空间不足、网络连接中断 |
| 5 | ERROR | 40 | 错误 | 函数调用失败、异常 |
| 6 | CRITICAL | 50 | 严重错误 | 程序崩溃 |
3.2、logging打印日志
import logging
logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.critical('critical')
#也可以通过logging.log指定日志等级。
logging.log(level=logging.ERROR,msg="log_test")
logging.log(level=100,msg="log_test_a")
输出如下:
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical
ERROR:root:log_test
Level 100:root:log_test_a
结果说明:
-
默认日志级别 warning:在默认情况下,只打印 warning 级别及以上的日志,低于该级别以下的日志信息将会被忽略
-
日志级别修改:可通过
logging.basiConfig进行调整(下面有介绍) -
root:日志记录器的名称,会随文件位置的改变而改变
3.3、logging.basicConfig配置
logging.basicConfig用于一次性设置日志系统的基本参数(如日志级别、输出格式、输出位置等)。只需调用一次,后续所有日志输出都会遵循这个配置,即使再次定义logging.basicConfig也不会生效。常见参数如下:
| 参数 | 作用说明 | 示例值 |
|---|---|---|
| level | 设置日志级别(只显示大于等于该级别的日志)。默认是WARING | logging.INFO, logging.DEBUG |
| filename | 指定日志输出到的文件名(不指定则输出到控制台) | ‘app.log’ |
| filemode | 文件写入模式,默认为 ‘a’(追加),可设为 ‘w’(覆盖) | ‘w’ |
| format | 日志输出格式字符串 | ‘%(asctime)s - %(levelname)s - %(message)s’ |
| datefmt | 时间格式字符串。不设置时默认时间格式为‘2025-11-14 15:24:51,668’ | ‘%Y-%m-%d %H:%M:%S’ |
| encoding | 文件编码(仅当指定 filename 时有效) | ‘utf-8’ |
| stream | 指定日志应写入哪个流对象(例如,一个文件对象或 sys.stderr)。如果指定了这个参数,则不会使用 filename 参数。 |
|
| handlers | 如果已经创建了处理程序(handler),则可以将它们作为此参数的列表传递。 |
如果不调用basicConfig(),日志系统有默认配置:
-
日志级别为
WARNING,只显示WARNING及以上级别的日志。 -
日志输出到控制台。
-
日志格式为:
LEVEL:logger_name:message,如:WARNING:root:demo
import logging
logging.warning('Started')
logging.warning('Finished')
输出为:
WARNING:root:Started
WARNING:root:Finished
调用了basicConfig(),则按照配置的basicConfig显示日志
import logging
logging.basicConfig(level=logging.INFO, filename="log_file.log", filemode="a", format='%(asctime)s %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p', encoding="UTF-8")
logging.info('Started')
logging.info('Finished')
当前目录下的生成一个文件名为log_file.log的文件,内容如下:
10/15/2025 03:22:09 PM Started
10/15/2025 03:22:09 PM Finished
3.4、format的常见参数
| 字段 | 说明 | 示例输出 |
|---|---|---|
| %(asctime)s | 日志创建时间 | 2023-01-01 12:00:00,123 |
| %(levelname)s | 日志级别名称 | INFO |
| %(message)s | 日志消息内容 | 程序启动成功 |
| %(name)s | logger 的名字 | my_logger |
| %(filename)s | 生成日志的文件名 | app.py |
| %(lineno)d | 生成日志的行号 | 42 |
| %(funcName)s | 生成日志的函数名 | main |
| %(pathname)s | 生成源文件的完整路径 | |
| %(module)s | 生成模块名(文件名去掉 .py) | |
| %(process)d | 生成进程ID | |
| %(thread)d | 生成线程 ID |
import logging
class logging_sample():
logging.basicConfig(level=logging.INFO, filemode="a", filename="log_file.log",
format='[%(asctime)s] [%(levelname)s] [%(pathname)s %(filename)s %(module)s %(funcName)s:%('
'lineno)d] %(name)s %(process)d %(thread)d %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p', encoding="UTF-8")
def logging_test(self):
logging.info('Started')
logging.info('Finished')
if __name__ == '__main__':
logging_sample().logging_test()
执行后再同级目录下生成了一个文件名为log_file.log的文件,文件内容如下:
[10/15/2025 03:48:26 PM] [INFO] [C:\Users\python_logging\sample.py sample.py sample logging_test:19] root 19032 23648 Started
[10/15/2025 03:48:26 PM] [INFO] [C:\Users\python_logging\sample.py sample.py sample logging_test:20] root 19032 23648 Finished
如果设置了datefmt想要时间格式输出为毫秒,可以修改format的asctime格式:
logging.basicConfig(format=[%(asctime)s.%(msecs)03d] %(message)s',datefmt='%H:%M:%S')
时间格式输出结果为:15:37:21.640
四、logging的进阶用法
logging 四个组成部分
logging 模块基于一个经典的 生产者-过滤器-处理器 模型,主要由四个核心对象构成:
| 类 | 说明 | 示例 |
|---|---|---|
| logging.Logger | 记录器,用于发出日志消息 | logger = logging.getLogger("my_logger") |
| logging.Handler | 处理器,决定日志输出位置(如文件、控制台等) | handler = logging.FileHandler("app.log") |
| logging.Formatter | 格式化器,控制日志输出的格式 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') |
| logging.Filter | 过滤器,用于更精细地控制日志记录 | filter = logging.Filter("module.name") |
4.1、logging.Logger
负责创建一个日志收集器,默认的名称为root。同一个名称的日志收集器的对应的实例也是同一个。即只要logging.getLogger(name)中名称参数name相同则返回的Logger实例就是同一个,且仅有一个,也即name与Logger实例一一对应。
Logger是一个树形层级结构,输出信息之前都要获得一个Logger,如果沒有获得logger,则自动创建并且使用root的logger,比如3.2、logging打印日志的例子。logger = logging.getLogger()返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置。指定了name的logger实际上其父logger为root,比如通过logger = logging.getLogger()显示的创建了root Logger,而logger1 = logging.getLogger('mylogger')创建了root Logger的子级(root.)mylogger,子集会将消息分发给他的handler进行处理也会传递给所有的祖先Logger处理。所以我们创建日志收集器的时候一定要指定name,否则会修改root的logger默认配置,并且某些第三方库也使用了根日志记录器,并且它们的日志级别被设为了 DEBUG,那么你就会看到很多 DEBUG 级别的输出。并且一个文件中最好不要存在父子级的多个日志收集器,不然输出的日志会重复。
import logging
# 创建一个logger
logger = logging.getLogger()
logger1 = logging.getLogger('mylogger')
#设置日志收集器的等级
logger1.setLevel(logging.INFO)
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log')
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
# 定义handler的输出格式formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
#日志收集器对接输出渠道
logger.addHandler(fh)
logger.addHandler(ch)
logger1.addHandler(fh)
logger1.addHandler(ch)
# 记录一条日志
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
logger1.debug('logger1 debug message')
logger1.info('logger1 info message')
logger1.warning('logger1 warning message')
logger1.error('logger1 error message')
logger1.critical('logger1 critical message')
文件和控制台输出为:
2025-10-15 18:00:18,566 - root - WARNING - logger warning message
2025-10-15 18:00:18,566 - root - ERROR - logger error message
2025-10-15 18:00:18,567 - root - CRITICAL - logger critical message
2025-10-15 18:00:18,567 - mylogger - INFO - logger1 info message
2025-10-15 18:00:18,567 - mylogger - INFO - logger1 info message
2025-10-15 18:00:18,567 - mylogger - WARNING - logger1 warning message
2025-10-15 18:00:18,567 - mylogger - WARNING - logger1 warning message
2025-10-15 18:00:18,567 - mylogger - ERROR - logger1 error message
2025-10-15 18:00:18,567 - mylogger - ERROR - logger1 error message
2025-10-15 18:00:18,567 - mylogger - CRITICAL - logger1 critical message
2025-10-15 18:00:18,567 - mylogger - CRITICAL - logger1 critical message
一般推荐的常见日志收集器的命令为:logging.getLogger(__name__) 创建,__name__ 是模块的全名。
关键属性:
-
名称(Name):通常使用 logging.getLogger(name) 创建,name 是模块的全名(如 myapp.module),这有助于组织日志层次结构。
-
级别(Level):Logger 有一个日志级别。只有级别大于或等于此级别的日志记录才会被处理。如果 Logger 本身没有设置级别,它会向上传递到父 Logger(最终到根 Logger)。通过obj.setLevel(logging.XXX)来设置
4.2、logging.Handler
决定日志消息的输出,比如控制台、文件等。一个 Logger 可以有多个 Handler。
常见的handler为:
-
StreamHandler:输出到流(如 sys.stdout, sys.stderr)。
-
FileHandler:输出到文件。
-
RotatingFileHandler:输出到文件,并在文件达到一定大小时轮转。
-
TimedRotatingFileHandler:按时间(如每天)轮转日志文件。
-
SMTPHandler:通过邮件发送日志。
-
SocketHandler :通过网络发送日志
关键属性:
-
级别(Level):Handler 也有自己的级别。即使 Logger 接受了一条日志,Handler 也会根据自己的级别再次过滤。例如,Logger 级别是 DEBUG,Handler 级别是 INFO,那么 DEBUG 级别的日志不会被这个 Handler 处理。通过obj.setLevel(logging.XXX)来设置
-
Formatter:关联一个 Formatter 来格式化日志消息。通过obj.setFormatter(formatter)来设置
-
FileHandler 会无限增长文件。使用 RotatingFileHandler 或 TimedRotatingFileHandler 可以自动管理日志文件大小和数量。
4.3、logging.Formatter
设置日志消息的最终格式,格式一般为使用类似 %(asctime)s - %(name)s - %(levelname)s - %(message)s 的字符串。可以给不同的handler设置不同的Formatter。可能用到的格式化串参见上文(logging.basicConfig()函数format参数)
4.4、logging.Filter
提供更精细的日志过滤机制,可以基于 Logger 名称、日志级别或其他属性进行过滤。一般使用比较少。比如我们定义了filter = logging.Filter('a.b.c'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带a.b.c前缀的Logger才能输出其日志。当然也可以直接给Logger加Filter。若为Handler加Filter则所有使用了该Handler的Logger都会受到影响。而为Logger添加Filter只会影响到自身。
4.5、logging.conf
除了直接在程序中设置Logger,Handler,Filter,Formatter外还可以将这些信息写进配置文件中。名称为logging.conf,格式如下:
[loggers]
keys=root,fileLogger,rotatingFileLogger
[handlers]
keys=consoleHandler,fileHandler,rotatingFileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_fileLogger]
level=DEBUG
# 该logger中配置的handler
handlers=fileHandler
# logger 的名称
qualname=fileLogger
propagate=0
[logger_rotatingFileLogger]
level=DEBUG
# 这样配置,rotatingFileLogger中就同时配置了consoleHandler,rotatingFileHandler
# consoleHandler 负责将日志输出到控制台
# rotatingFileHandler 负责将日志输出保存到文件中
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('logs/logging.log', 'a')
[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=("logs/rotating_logging.log", "a", 1*1024*1024, 5)
[formatter_simpleFormatter]
#format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
format=[%(asctime)s]-[%(levelname)s]-[%(filename)s]-[%(funcName)s:%(lineno)d] : %(message)s
datefmt=%Y-%m-%d %H:%M:%S
以上配置文件主要包含以下几部分:
loggers : 配置logger信息。必须包含一个名字叫做root的logger,当使用无参函数logging.getLogger()时,默认返回root这个logger,其他自定义logger可以通过 logging.getLogger("fileLogger") 方式进行调用 handlers:定义声明handlers信息。常用的handlers包括 StreamHandler(仅将日志输出到kong控制台)、FileHandler(将日志信息输出保存到文件)、RotaRotatingFileHandler(将日志输出保存到文件中,并设置单个日志文件的大小和日志文件个数) formatter : 设置日志格式 logger_xxx : 对loggers中声明的logger进行逐个配置,且要一一对应。声明level,handlers,qualname(logger的名称)、propagate(是否将传递给所有父Logger处理) handler_xxx : 对handlers中声明的handler进行逐个配置,且要一一对应。声明class、level、formatter、args formatter_xxx : 对声明的formatter文件进行配置。声明format和datefmt
logging.conf的调用
import os
import logging.config
logging_conf = os.path.join(os.getcwd(), "logging.conf")
# 避免出现格式解析问题
with open(logging_conf, 'r', encoding='utf-8') as f:
logging.config.fileConfig(f)
# rotatingFileLogger中额consoleHandler输出到控制台,rotatingHandler输出日志到文件
rotating_logger = logging.getLogger(name="rotatingFileLogger")
rotating_logger.critical('logger critical message')
输出结果为:
[2025-10-15 19:51:17]-[CRITICAL]-[logging_conf_demo.py]-[<module>:27] : logger critical message
五、logging的封装
自动化测试时会封装一个logging,然后testcase运行时统一调用。目录结构为logger_utils.py在common目录下,生成的日志目录为同级的log。然后在testcase中调用logger_utils,然后在步骤中加上对应的日志就行。
import logging
import os
import datetime
class Loggers:
def __init__(self):
# 设置输出格式
formater = logging.Formatter(
'[%(asctime)s]-[%(levelname)s]-[%(filename)s]-[%(funcName)s:%(lineno)d] %(message)s')
# 定义一个日志收集器
self.logger = logging.getLogger('autotest_log')
# 设定级别
self.logger.setLevel(logging.DEBUG)
# 输出方式1 - 文件形式
sh = self.fileLogger = logging.FileHandler(log_path, encoding='utf-8')
# 输出方式2 - 控制台
ch = self.console = logging.StreamHandler()
# 设置console和file输出的日志级别
sh.setLevel(logging.DEBUG)
ch.setLevel(logging.DEBUG)
# 对输出方式设置日志格式
sh.setFormatter(formater)
ch.setFormatter(formater)
# 日志收集器添加输出方式
self.logger.addHandler(sh)
self.logger.addHandler(ch)
def debug(self, msg):
self.logger.debug(msg=msg)
def info(self, msg):
self.logger.info(msg=msg)
def warn(self, msg):
self.logger.warning(msg=msg)
def error(self, msg):
self.logger.error(msg=msg)
def critical(self, msg):
self.logger.critical(msg=msg)
# 指定文件目录路径
log_dir = os.path.join(os.path.dirname(__file__), "../log")
# 判断文件是否存在,不存在就新建
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 指定文件名称和文件路径
filename = 'autotest' + datetime.datetime.now().strftime('%Y%m%d') + '.log'
log_path = os.path.abspath(os.path.join(log_dir, filename))
# 实例化的时候需要带上logger,如果用log = Loggers()实例化,打印出来的日志line数为logger_utils.py的行数
log = Loggers().logger
if __name__ == '__main__':
log.debug('debug')
log.info('info')
log.info('-' * 5 + '返回状态码是' + str(re.status_code) + '-' * 5)
log.info('-' * 5 + '返回结果集是' + re.text + '-' * 5)
参考文档:
https://blog.csdn.net/cxx654/article/details/83216337
https://blog.csdn.net/zyz511919766/article/details/25136485
https://blog.csdn.net/weixin_51924657/article/details/150420755
更多推荐
所有评论(0)