咳咳,各位观众老爷们,晚上好!欢迎来到今天的“Python高级技术茶话会”。今天咱们要聊的是Python里一个非常重要的模块——logging
。这玩意儿,说白了,就是用来记录程序运行过程中的各种信息的。可别小看它,用好了,能让你的程序bug无处遁形,简直就是程序员的“秘密武器”。
咱们的目标是:设计一个可扩展、可配置的日志系统,让它能适应各种奇葩的需求。
一、logging
模块的基本概念:先打个地基
要盖房子,先得打地基。logging
模块也一样,先得了解它的几个核心组件:
-
Logger(日志器): 这是
logging
模块的入口,相当于日志系统的“总指挥”。你创建一个logger实例,然后告诉它你想记录哪些信息。 -
Handler(处理器): Logger拿到日志信息后,并不会自己动手,而是交给Handler来处理。Handler负责把日志信息输出到不同的地方,比如控制台、文件、网络等等。
-
Formatter(格式器): Handler拿到日志信息后,还需要对它进行格式化,才能输出成你想要的样子。Formatter就是干这个的,它可以把日志信息格式化成字符串,然后交给Handler输出。
-
Filter(过滤器): 有时候,你只想记录某些特定的日志信息,这时候就可以用Filter来过滤掉不需要的信息。
-
Log Record(日志记录): 这个就是实际要记录的日志信息,包含了时间、级别、消息内容等等。
为了方便理解,咱们用一张表来总结一下:
组件 | 作用 |
---|---|
Logger | 日志系统的入口,负责接收和处理日志信息。 |
Handler | 负责将日志信息输出到不同的目标,比如控制台、文件、网络等等。 |
Formatter | 负责将日志信息格式化成字符串,以便输出。 |
Filter | 负责过滤掉不需要的日志信息。 |
Log Record | 包含实际要记录的日志信息,比如时间、级别、消息内容等等。 |
二、logging
模块的基本用法:撸起袖子干
光说不练假把式,咱们来写几个简单的例子:
import logging
# 创建一个logger实例
logger = logging.getLogger(__name__)
# 设置日志级别
logger.setLevel(logging.DEBUG)
# 创建一个handler,用于输出到控制台
console_handler = logging.StreamHandler()
# 创建一个formatter,用于格式化日志信息
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 将formatter添加到handler
console_handler.setFormatter(formatter)
# 将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')
这段代码会把各种级别的日志信息输出到控制台。注意,只有级别大于等于logger设置的级别的日志信息才会被输出。
三、可扩展性设计:让日志系统能屈能伸
要让日志系统具有可扩展性,关键在于使用配置来驱动。咱们可以把日志系统的配置信息放到一个配置文件里,然后让程序在启动的时候读取这个配置文件,根据配置信息来创建logger、handler、formatter等等。
这样做的好处是:
- 灵活性: 你可以随时修改配置文件,而不需要修改代码,就能改变日志系统的行为。
- 可维护性: 配置文件比代码更容易维护,也更容易理解。
- 可重用性: 你可以把配置文件复制到不同的项目中使用,而不需要重复编写代码。
下面是一个简单的配置文件示例(logging.conf
):
[loggers]
keys=root,example
[handlers]
keys=consoleHandler,fileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler
[logger_example]
level=INFO
handlers=fileHandler
qualname=example
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=('example.log','a')
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
然后,咱们可以用logging.config.fileConfig
函数来读取这个配置文件:
import logging
import logging.config
import sys
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('example')
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')
root_logger = logging.getLogger()
root_logger.debug("This is a root logger debug message")
这段代码会根据logging.conf
文件中的配置信息,创建logger和handler,然后记录一些日志信息。
四、自定义Handler:打造专属的日志输出渠道
logging
模块自带的handler可能无法满足你的所有需求,这时候就需要自定义handler了。自定义handler很简单,只需要继承logging.Handler
类,然后重写emit
方法即可。
emit
方法是handler的核心,它负责把日志信息输出到目标。
下面是一个自定义handler的例子,它可以把日志信息发送到邮件:
import logging
import smtplib
from email.mime.text import MIMEText
class EmailHandler(logging.Handler):
def __init__(self, mail_from, mail_to, subject, smtp_server, smtp_port, username=None, password=None):
super().__init__()
self.mail_from = mail_from
self.mail_to = mail_to
self.subject = subject
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.username = username
self.password = password
def emit(self, record):
try:
msg = MIMEText(self.format(record))
msg['Subject'] = self.subject
msg['From'] = self.mail_from
msg['To'] = self.mail_to
s = smtplib.SMTP(self.smtp_server, self.smtp_port)
if self.username and self.password:
s.starttls() # Secure the connection
s.login(self.username, self.password)
s.sendmail(self.mail_from, self.mail_to, msg.as_string())
s.quit()
except Exception:
self.handleError(record)
# 使用方法:
# 创建一个EmailHandler实例
email_handler = EmailHandler(
mail_from='[email protected]',
mail_to='[email protected]',
subject='An error occurred!',
smtp_server='smtp.example.com',
smtp_port=587,
username='[email protected]',
password='your_password'
)
# 设置日志级别
email_handler.setLevel(logging.ERROR)
# 创建一个formatter,用于格式化日志信息
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 将formatter添加到handler
email_handler.setFormatter(formatter)
# 获取logger实例
logger = logging.getLogger('my_app')
logger.addHandler(email_handler)
logger.setLevel(logging.DEBUG) # Make sure logger level is at least ERROR to trigger email
# 记录一个错误日志
logger.error('Something went wrong!')
这个例子中,EmailHandler
会把错误日志信息发送到指定的邮箱。注意,你需要根据自己的实际情况修改mail_from
、mail_to
、subject
、smtp_server
、smtp_port
、username
和password
等参数。
五、自定义Filter:精准定位你想要的日志
有时候,你只想记录某些特定的日志信息,这时候就需要自定义filter了。自定义filter也很简单,只需要继承logging.Filter
类,然后重写filter
方法即可。
filter
方法接收一个LogRecord
对象作为参数,返回True
表示允许记录该日志信息,返回False
表示拒绝记录该日志信息。
下面是一个自定义filter的例子,它可以只记录来自特定模块的日志信息:
import logging
class ModuleFilter(logging.Filter):
def __init__(self, module_name):
super().__init__()
self.module_name = module_name
def filter(self, record):
return record.name == self.module_name
# 使用方法:
# 创建一个ModuleFilter实例
module_filter = ModuleFilter('my_module')
# 获取logger实例
logger = logging.getLogger('my_module')
# 添加filter
logger.addFilter(module_filter)
# 记录一些日志信息
logger.debug('This is a debug message from my_module') # 会被记录
logger.info('This is an info message from my_module') # 会被记录
other_logger = logging.getLogger('other_module')
other_logger.debug('This is a debug message from other_module') # 不会被记录
这个例子中,ModuleFilter
只会记录来自my_module
模块的日志信息。
六、高级技巧:玩转logging
模块
-
使用
logging.getLogger(name)
创建logger实例:name
参数可以用来区分不同的logger,方便你管理日志信息。一般来说,name
参数应该设置为模块名,也就是__name__
。 -
使用
logging.basicConfig()
快速配置日志系统: 如果你不想手动创建logger、handler、formatter等等,可以使用logging.basicConfig()
函数来快速配置日志系统。但是,logging.basicConfig()
函数只能配置一次,而且它的配置信息会被覆盖。 -
使用
logging.exception()
记录异常信息:logging.exception()
函数可以记录异常信息,并且会自动把异常的堆栈信息也记录下来。这对于调试程序非常有用。 -
使用
logging.getLoggerClass()
和logging.setLoggerClass()
自定义logger类: 如果你需要对logger进行更高级的定制,可以自定义logger类。
七、最佳实践:让你的日志系统更上一层楼
-
为每个模块创建一个logger实例: 这样可以方便你根据模块来过滤日志信息。
-
使用不同的日志级别来区分不同类型的信息: 比如,debug级别的日志信息可以用来调试程序,info级别的日志信息可以用来记录程序的运行状态,warning级别的日志信息可以用来提示潜在的问题,error级别的日志信息可以用来记录错误信息,critical级别的日志信息可以用来记录严重的错误信息。
-
在配置文件中配置日志系统: 这样可以方便你修改日志系统的行为,而不需要修改代码。
-
定期清理日志文件: 日志文件会占用大量的磁盘空间,所以需要定期清理。你可以使用
logrotate
等工具来自动清理日志文件。 -
使用结构化日志: 结构化日志可以让你更容易地分析和处理日志信息。你可以使用JSON格式来记录日志信息。
八、总结:打造你的专属日志系统
今天咱们聊了logging
模块的基本概念、基本用法、可扩展性设计、自定义handler、自定义filter和高级技巧。希望这些内容能帮助你打造一个可扩展、可配置的日志系统,让你的程序bug无处遁形。
记住,logging
模块是一个非常强大的工具,用好了,能让你的编程工作事半功倍。
好了,今天的茶话会就到这里。感谢各位观众老爷的观看,咱们下次再见!