Python的logging
模块是一个内置的标准库,它为编写程序时生成、记录和管理日志信息提供了强大而灵活的功能。日志对于软件开发至关重要,尤其是在调试、监控应用状态、追踪异常行为、分析性能瓶颈以及审计等方面。
入门级的logging
应用主要是掌握如何在简单的Python程序中引入logging
模块,设置基本的日志级别,记录日志信息,并选择适当的输出目的地(如控制台)。本篇文章就讲一讲logging的入门到进阶:
目录
1)、使用 RotatingFileHandler 实现按文件大小轮转
2)、使用 TimedRotatingFileHandler 实现按时间间隔轮转
一、日志级别
logging模块定义了多个日志级别,按严重性递增排列如下:
- DEBUG: 提供详细的内部信息,用于调试目的。
- INFO: 记录常规的运行信息,如系统状态、用户行为等。
- WARNING: 表示潜在的问题或非预期的情况,但程序仍能继续运行。
- ERROR: 报告程序运行时发生的错误,导致某些功能无法正常工作。
- CRITICAL: 指示严重的故障或系统即将崩溃的情况。
每个级别都对应一个整数值,值越大表示严重性越高。日志消息会被分配一个级别,只有当它的级别等于或高于所设置的日志记录器(logger)的阈值时,才会被实际记录和处理。
二、组件与工作流程
logging
模块的核心组件包括:
- Logger: 用于收集和分发日志事件。每个logger对象代表了程序中的某个部分或模块,可以独立设置日志级别和添加处理器。日志消息首先被发送到相应的logger。
- Handler: 负责将logger传递过来的日志消息输出到适当的目的地,如控制台、文件、数据库等。常见的Handler类型有StreamHandler(输出到流,如sys.stdout或sys.stderr)、FileHandler(写入文件)、SMTPHandler(发送电子邮件)等。每个handler可以有自己的日志级别和格式化器。
- Formatter: 定义日志消息的输出格式,包括时间戳、日志级别、消息内容、源文件名和行号等信息。可以自定义格式字符串以满足特定需求。
- Filter: 可选组件,用于对日志消息进行额外的筛选。可以基于特定条件决定是否让某条日志消息通过,进一步细化日志记录的控制。
日志消息的处理流程大致如下:
用户通过logger对象调用相应级别的方法(如logger.debug()、logger.info()等)记录日志。
如果该消息级别不低于logger的阈值,消息会被传递给所有关联的handler。
handler检查消息级别是否达到自己的阈值,如果是,则使用关联的formatter格式化消息,并将格式化后的消息发送到指定的目标。
三、基本使用示例
1、导入logging模块并配置日志记录器
对于简单应用,可以使用logging.basicConfig()
进行快速全局配置。例如:
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logging.basicConfig(
level=logging.INFO, # 设置日志级别为INFO
format='%(asctime)s - %(levelname)s - %(message)s', # 设置日志格式
datefmt='%Y-%m-%d %H:%M:%S', # 设置时间格式
stream=sys.stdout, # 输出到标准输出(控制台)
)
在这个配置中:
- level=logging.INFO:设置日志级别为INFO,这意味着DEBUG级别的日志将不会被记录。你可以根据需要调整为DEBUG、WARNING、ERROR等其他级别。
- format='%(asctime)s - %(levelname)s - %(message)s':定义日志的输出格式,包含时间戳、日志级别和消息内容。%(asctime)s表示时间戳,%(levelname)s表示日志级别,%(message)s表示消息正文。
- datefmt='%Y-%m-%d %H:%M:%S':指定时间戳的显示格式。
- stream=sys.stdout:指定日志输出到标准输出(即控制台)。若要改为输出到标准错误(stderr),可以使用stream=sys.stderr。
指定了一个基本的日志格式,所有未显式配置的logger
都将继承这些设置。
2、创建日志记录器实例
尽管basicConfig()
已经设置了全局的日志行为,但对于更复杂的项目,通常建议创建特定的logger
实例。这样可以为不同的模块或组件赋予独立的日志记录器,便于管理和区分日志来源。创建logger
实例如下:
logger = logging.getLogger(__name__)
这里使用__name__
作为logger
的名称,它会自动填充为当前模块的名称,有助于在多模块环境中识别日志来源。就相当于给指定模块或者类加上标签,如果你的程序很大,可能希望不同的模块或类各自有日志记录,这样查起来更方便。
import logging
module_logger = logging.getLogger('MyModule')
module_logger.info('这是MyModule模块的日记')
这样,日记前面就有标签了,一看就知道是哪个部分写的。下面是一个简单的例子,展示如何为不同模块或类创建带标签的logger
实例,并记录日志:
project/
│
├── main.py
├── module_a.py
└── module_b.py
#主程序结构示例
main.py (主程序):
import logging
import module_a
import module_b
# 基本日志配置
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
stream=sys.stdout,
)
# 调用模块A和模块B的功能
module_a.do_something()
module_b.do_something_else()
module_a.py (模块A):
import logging
# 创建模块A的日志记录器,标签为'module_a'
module_a_logger = logging.getLogger('module_a')
def do_something():
module_a_logger.info('模块A开始执行操作...')
# ... 执行具体操作 ...
module_a_logger.info('模块A操作完成')
module_b.py (模块B):
import logging
# 创建模块B的日志记录器,标签为'module_b'
module_b_logger = logging.getLogger('module_b')
def do_something_else():
module_b_logger.info('模块B开始执行操作...')
# ... 执行具体操作 ...
module_b_logger.info('模块B操作完成')
在这个例子中:
- main.py 是主程序,导入了模块module_a和module_b,并进行了基本的日志配置。配置中使用%(name)s来显示日志记录器的名称(即标签)。
- module_a.py 和 module_b.py 分别代表两个不同的模块。在每个模块中,我们创建了一个带有特定标签的logger实例(如module_a_logger和module_b_logger),标签对应模块的名称。
- 模块中的函数(如do_something()和do_something_else())在执行关键操作前后,使用各自的logger实例记录日志信息,标签确保了日志消息能够明确标识出它们来自哪个模块。
当你运行main.py时,控制台会输出类似这样的日志:
2024-08-9 10:00:00 - module_a - INFO - 模块A开始执行操作...
2024-08-9 10:00:01 - module_a - INFO - 模块A操作完成
2024-08-9 10:00:02 - module_b - INFO - 模块B开始执行操作...
2024-08-9 10:00:03 - module_b - INFO - 模块B操作完成
每个日志消息都带有标签(如module_a
和module_b
),清晰地表明了它们分别来自哪个模块。这种做法有助于你在查看日志时快速定位问题所在,特别是在大型项目中,具有显著的便利性。
3、记录日志信息
有了配置好的logger
实例,就可以在代码中使用不同级别的方法来记录日志了。例如:
logger.debug('This is a debug message.')
logger.info('This is an info message.')
logger.warning('This is a warning message.')
logger.error('This is an error message.')
logger.critical('This is a critical message.')
这些方法对应于之前设置的日志级别。只有级别等于或高于已设置日志级别的消息才会被实际记录和输出。
4、查看日志输出
运行程序,你会在控制台看到类似这样的日志输出:
2024-03-22 15: - This is an info message.
2024-03-22 15:22:34 - WARNING - This is a warning message.
2024-03-22 15:22:34 - ERROR - This is an error message.
2024-03-22 15:22:34 - CRITICAL - This is a critical message.
5、保存日志到文件
如果希望将日志保存到文件而不是仅输出到控制台,可以使用FileHandler
替stream=sys.stdout
。修改basicConfig()
如下:
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
filename='app.log', # 将日志保存到文件'app.log'
)
现在,日志信息将被写入到指定的app.log
文件中,而不是控制台。
四、进阶
随着对logging
模块的深入使用,可以进一步学习和应用更复杂的日志处理技术,如自定义日志格式、使用不同的处理器、设置日志记录器层次结构、实现日志文件轮转等等。
1、自定义日志格式
自定义日志格式是指在使用Python的logging
模块时,根据实际需求更改日志消息的输出样式。这可以通过创建一个logging.Formatter
对象并设置其格式字符串来实现。格式字符串使用特殊的占位符(如%(asctime)s
、%(levelname)s
等)来插入日志消息中的不同属性。以下是如何自定义日志格式的实例:
import logging
# 创建自定义格式器
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
# 创建Handler,将Formatter与之关联
console_handler = logging.StreamHandler() # 输出到控制台
console_handler.setFormatter(formatter)
# 获取或创建Logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # 设置日志级别
# 将Handler添加到Logger
logger.addHandler(console_handler)
# 记录日志
logger.debug('This is a debug message.')
logger.info('This is an info message.')
logger.warning('This is a warning message.')
logger.error('This is an error message.')
logger.critical('This is a critical message.')
运行这段代码,你将在控制台看到按照自定义格式输出的日志消息。同样,如果使用了FileHandler
,日志消息将以同样的格式写入指定的文件。通过这种方式,你可以根据项目需求灵活调整日志的输出样式,使其更加符合团队约定或易于后期分析。
logging.Formatter
传入自定义的格式字符串,格式字符串中可以包含以下常用的占位符:
%(asctime)s
: 时间戳。默认格式类似2003-07-08 16:49:45,896
,可以通过datefmt
参数进一步自定义。%(levelname)s
: 日志级别名称,如DEBUG
、INFO
、WARNING
、ERROR
或CRITICAL
。%(name)s
: 记录器名称(即logger
实例的名称)。%(message)s
: 日志消息正文。%(module)s
: 发出日志消息的模块名。%(funcName)s
: 发出日志消息的函数名。%(lineno)d
: 发出日志消息的源代码行号。%(thread)d
或%(threadName)s
: 当前线程ID或名称。%(process)d
或%(processName)s
: 当前进程ID或名称。- 其他自定义字段(如果在日志记录时添加了额外的元数据)。
2、设置日志记录器层次结构
logging
模块中的日志记录器(logger
)支持层次结构的概念,这意味着记录器可以组织成一个树状结构,通过名称的点分隔表示父子关系。这种层次结构使得日志管理更为灵活,允许在不同粒度上设置日志级别、过滤规则等配置。以下是设置日志记录器层次结构的步骤和示例:
import logging
# 创建处理器,输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 控制台只输出INFO及以上级别
# 设置根记录器级别为INFO
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
root_logger.addHandler(console_handler)
# 模块A记录器,级别为DEBUG
module_a_logger = logging.getLogger('module_a')
module_a_logger.setLevel(logging.DEBUG) # 模块A记录更详细的日志
# 模块A内部子模块记录器,继承模块A的级别
module_a_submodule_logger = logging.getLogger('module_a.submodule')
# 使用记录器记录日志
module_a_logger.debug('Debug message from module A')
module_a_submodule_logger.info('Info message from submodule of module A')
运行这段代码,控制台将只输出module_a.submodule
的info
消息,因为这是唯一高于处理器级别(INFO
)的日志。module_a
的debug
消息虽然其记录器级别允许,但由于处理器级别限制,不会被输出。
通过这样的层次结构设置,可以方便地在不同层级上管理日志记录,如全局设置根记录器的级别以控制整体日志输出量,然后在特定模块或子系统中细化日志级别以获取更多调试信息。这种结构也使得日志过滤、路由到不同输出目的地等操作更加灵活和高效。
3、使用不同的处理器
使用不同的处理器(Handler)可以让Python的logging
模块将日志消息输出到多种不同的目的地,如控制台、文件、电子邮件、数据库等。每个处理器负责一种特定的输出方式,并且可以独立配置其日志级别、输出格式和过滤条件。以下是使用不同处理器的步骤和示例:
import logging
from logging import handlers
# 创建处理器
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler('application.log')
# 设置处理器级别
console_handler.setLevel(logging.INFO)
file_handler.setLevel(logging.DEBUG)
# 创建并设置格式器
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 获取或创建记录器
logger = logging.getLogger('my_application')
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 使用记录器记录日志
logger.debug('Debug message')
logger.info('Info message')
logger.warning('Warning message')
logger.error('Error message')
logger.critical('Critical message')
运行这段代码,INFO
及以上级别的日志消息将输出到控制台,而所有级别的日志消息(包括DEBUG
)都将写入到application.log
文件中。通过使用不同的处理器,可以将日志消息路由到多种目的地,以满足不同的监控、分析和归档需求。
常见的处理器类型包括:
logging.StreamHandler
: 输出到标准输出(sys.stdout
)或标准错误(sys.stderr
)。logging.FileHandler
: 将日志写入到指定的文件。logging.handlers.RotatingFileHandler
: 当文件达到一定大小时,自动创建新的日志文件,并可选择保留旧文件。logging.handlers.TimedRotatingFileHandler
: 按照固定时间间隔(如每天、每周)自动创建新的日志文件。logging.handlers.SMTPHandler
: 发送日志邮件。logging.handlers.HTTPHandler
: 将日志发送到HTTP服务器。
4、实现日志文件轮转
日志文件轮转(log file rotation)是一种常见的日志管理策略,当日志文件达到一定大小或达到特定时间点时,自动创建新的日志文件,同时可以保留旧日志文件以供后续查阅或归档。在Python的logging
模块中,可以使用logging.handlers.RotatingFileHandler
或logging.handlers.TimedRotatingFileHandler
来实现日志文件轮转。以下是两种方式的实现示例:
1)、使用 RotatingFileHandler
实现按文件大小轮转
RotatingFileHandler
会在日志文件达到指定大小时自动创建新的日志文件。你可以设置最大文件大小、备份文件数量以及备份文件名的模式:
import logging
from logging.handlers import RotatingFileHandler
# 创建RotatingFileHandler实例
rotating_handler = RotatingFileHandler('app.log', maxBytes=10_000_000, backupCount=5)
# 设置处理器级别、格式器等
rotating_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
rotating_handler.setFormatter(formatter)
# 获取或创建记录器
logger = logging.getLogger('my_app')
# 添加处理器
logger.addHandler(rotating_handler)
# 使用记录器记录日志
logger.debug('Debug message')
logger.info('Info message')
logger.warning('Warning message')
logger.error('Error message')
logger.critical('Critical message')
在这个例子中:
- RotatingFileHandler('app.log', maxBytes=10_000_000, backupCount=5) 创建了一个RotatingFileHandler,指定主日志文件名为app.log,当文件大小达到10MB时触发轮转,最多保留5个备份文件。
- 当日志文件达到最大大小时,RotatingFileHandler会自动创建一个新文件(如app.log.1),并将当前app.log重命名为app.log.2(如果存在app.log.1,则依次向后重命名)。依此类推,直至达到备份文件数量上限,超出的最旧文件将被删除。
2)、使用 TimedRotatingFileHandler
实现按时间间隔轮转
TimedRotatingFileHandler
会在指定的时间间隔到达时自动创建新的日志文件。你可以设置轮转周期(如按小时、天、周、月等),以及备份文件的模式:
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建TimedRotatingFileHandler实例
timed_rotating_handler = TimedRotatingFileHandler('app.log', when='midnight', backupCount=7)
# 设置处理器级别、格式器等
timed_rotating_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
timed_rotating_handler.setFormatter(formatter)
# 获取或创建记录器
logger = logging.getLogger('my_app')
# 添加处理器
logger.addHandler(timed_rotating_handler)
# 使用记录器记录日志
logger.debug('Debug message')
logger.info('Info message')
logger.warning('Warning message')
logger.error('Error message')
logger.critical('Critical message')
在这个例子中:
- TimedRotatingFileHandler('app.log', when='midnight', backupCount=7) 创建了一个TimedRotatingFileHandler,指定主日志文件名为app.log,每天午夜(midnight)触发轮转,最多保留7个备份文件。
- 当轮转时间点到达时,TimedRotatingFileHandler会自动创建一个新文件(如app.log.2024-0¾-10),并将当前app.log重命名为备份文件。依此类推,直至达到备份文件数量上限,超出的最旧文件将被删除。
通过使用RotatingFileHandler或TimedRotatingFileHandler,可以根据实际需求轻松实现日志文件的轮转,避免单个日志文件过大,同时保留历史日志以供查阅或归档。
希望该篇文章能有效帮助大家理解应用logging模块~
标签:logging,记录,Python,message,module,模块,日志,logger From: https://blog.csdn.net/qq_52758588/article/details/136972944