赞
踩
在软件开发中,日志是一个不可或缺的部分。它不仅用于调试和错误排查,还可以用于监控应用程序的运行状态。Python提供了一个强大且灵活的日志库——logging
,可以满足几乎所有日志记录需求。本文将详细介绍Python的logging
库,包括其基本概念、配置方法、日志记录级别、处理器、格式化器,以及如何在实际应用中有效利用它。
Python的logging
库是标准库的一部分,旨在为应用程序提供灵活的日志记录功能。它可以轻松地记录不同级别的日志信息,并可以将日志信息输出到不同的目标,比如控制台、文件、远程服务器等。logging
库非常适合在开发和生产环境中使用,因为它不仅可以帮助开发人员追踪和调试代码,还可以帮助系统管理员监控应用程序的运行状态。
在深入了解logging
库的使用之前,需要了解几个基本概念:
StreamHandler
、FileHandler
等。在使用logging
库时,首先需要创建一个记录器,然后可以通过这个记录器记录日志消息。下面是一个简单的示例:
import logging # 创建一个记录器 logger = logging.getLogger('my_logger') logger.setLevel(logging.DEBUG) # 创建一个处理器 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # 创建一个格式化器并将其添加到处理器中 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) # 将处理器添加到记录器中 logger.addHandler(ch) # 记录一些日志消息 logger.debug('这是一个调试消息') logger.info('这是一个信息消息') logger.warning('这是一个警告消息') logger.error('这是一个错误消息') logger.critical('这是一个致命错误消息')
在上述示例中,创建了一个名为my_logger
的记录器,并将其日志级别设置为DEBUG。然后创建了一个StreamHandler
,用于将日志消息输出到控制台。接着,创建了一个Formatter
,定义了日志消息的格式。最后,将处理器和格式化器添加到记录器中,并记录了一些不同级别的日志消息。
setLevel(logging.DEBUG)
?在 Python 的 logging
模块中,有两个 setLevel(logging.DEBUG)
是因为日志记录的级别设置涉及到两个不同的层次:记录器(Logger)和处理器(Handler)。让我们详细讨论一下它们各自的作用和意义。
记录器是日志记录的核心,它决定了哪些日志消息会被处理和记录。记录器有一个级别,只有级别高于或等于这个级别的日志消息才会被处理。通过 logger.setLevel(logging.DEBUG)
,你设置了记录器的最低级别为 DEBUG,这意味着 DEBUG 及其以上级别的消息(DEBUG, INFO, WARNING, ERROR, CRITICAL)都会被处理。
处理器是将日志消息发送到合适的输出位置的组件,例如控制台、文件、网络等。每个处理器也有自己的级别设置,只有级别高于或等于处理器级别的日志消息才会被发送到处理器的输出位置。通过 ch.setLevel(logging.DEBUG)
,你设置了处理器的最低级别为 DEBUG,这意味着 DEBUG 及其以上级别的消息都会被这个处理器处理并输出。
setLevel(logging.DEBUG)
记录器级别设置:这是为了控制日志消息的初始过滤。只有级别符合条件的消息才会被进一步处理。这一层的设置确保了不必要的日志消息不会浪费资源。
处理器级别设置:这是为了控制具体的输出行为。即使一条日志消息通过了记录器的级别过滤,如果它的级别低于处理器的级别,它也不会被输出到该处理器。这一层的设置确保了不同的处理器可以有不同的日志输出级别。例如,你可能希望某些重要消息被写入文件,而所有消息都打印到控制台。
logging
库定义了五个标准的日志级别,每个级别都有对应的数值:
使用这些级别可以控制日志的记录和显示。比如,可以设置记录器只记录WARNING及以上级别的日志,而忽略DEBUG和INFO级别的日志。
logging.basicConfig()
实际上并没有显式地配置特定的处理器(Handler)。这个 basicConfig
方法在没有提供其他参数的情况下,会默认配置一个 StreamHandler
,它将日志输出到标准输出(通常是控制台)。这个默认的设置等同于创建了一个很基本的控制台日志处理器。
这里的 StreamHandler
是最简单的日志处理器之一,用于将日志消息发送到控制台。它会接收所有等于或高于它配置级别的日志消息(代码中为 DEBUG
级别),并将其输出到 sys.stdout
,即 Python 程序的标准输出。
logging.basicConfig
,配置日志级别和格式化日期)logging
库提供了一个简单的配置函数logging.basicConfig
,可以用来快速配置日志系统。下面是一个基本配置的示例:
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.debug('这是一个调试消息')
logging.info('这是一个信息消息')
logging.warning('这是一个警告消息')
logging.error('这是一个错误消息')
logging.critical('这是一个致命错误消息')
在上述示例中,使用basicConfig
函数设置了日志级别和日志格式。所有日志消息将输出到控制台,并且使用指定的格式。
level=
)import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('这是一个调试消息')
logging.info('这是一个信息消息')
logging.warning('这是一个警告消息')
logging.error('这是一个错误消息')
logging.critical('这是一个致命错误消息')
import logging
logging.basicConfig()
logging.debug('这是一个调试消息')
logging.info('这是一个信息消息')
logging.warning('这是一个警告消息')
logging.error('这是一个错误消息')
logging.critical('这是一个致命错误消息')
对于更复杂的日志需求,可以使用字典配置或配置文件来配置日志系统。以下是使用字典配置的示例:
import logging import logging.config log_config = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'standard', 'level': logging.DEBUG, }, 'file': { 'class': 'logging.FileHandler', 'filename': 'app.log', 'formatter': 'standard', 'level': logging.DEBUG, }, }, 'loggers': { '': { 'handlers': ['console', 'file'], 'level': logging.DEBUG, 'propagate': True, }, } } logging.config.dictConfig(log_config) logger = logging.getLogger(__name__) logger.debug('这是一个调试消息') logger.info('这是一个信息消息') logger.warning('这是一个警告消息') logger.error('这是一个错误消息') logger.critical('这是一个致命错误消息')
在上述示例中,使用字典配置了日志系统,包括格式化器、处理器和记录器。日志消息将同时输出到控制台和文件app.log
。
日志处理器是日志系统中负责将日志消息发送到不同目标的组件。logging
库提供了多种处理器,包括StreamHandler
、FileHandler
等。
StreamHandler
用于将日志消息输出到流(如控制台、文件等)。下面是一个使用StreamHandler
的示例:
import logging
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.debug('这是一个调试消息')
FileHandler
用于将日志消息输出到文件。下面是一个使用FileHandler
的示例:
import logging
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('my_log.log')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.debug('这是一个调试消息')
除了StreamHandler
和FileHandler
,logging
库还提供了其他多种处理器,比如:
RotatingFileHandler
:用于将日志消息输出到文件,并在文件达到一定大小时进行日志轮换。TimedRotatingFileHandler
:用于将日志消息输出到文件,并在特定时间间隔进行日志轮换。SocketHandler
:用于将日志消息发送到网络套接字。SMTPHandler
:用于通过电子邮件发送日志消息。日志格式化器用于定义日志消息的输出格式。logging
库的Formatter
类允许使用格式字符串自定义日志消息的格式。下面是一个示例:
import logging
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.debug('这是一个调试消息')
在上述示例中,使用格式字符串'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
定义了日志消息的格式,包括时间戳、记录器名称、日志级别和消息内容。
在Python的logging
模块中,日志格式化字符串可以包含多个特定的变量,这些变量会在日志记录时被自动替换。除了你提到的%(asctime)s
、%(name)s
、%(levelname)s
、%(message)s
,还有许多其他的变量可以使用。
可以在官方文档中找到所有可用的格式化变量:
%(asctime)s
:日志事件发生的时间,格式是可配置的,默认格式为'YYYY-MM-DD HH:MM:SS,sss'
。(asc
是 “ASCII” 的缩写。asctime
代表 “ASCII time”,即使用ASCII字符表示的时间字符串)%(levelname)s
:日志级别名称(如DEBUG
、INFO
、WARNING
、ERROR
、CRITICAL
)。%(name)s
:记录器的名称。%(message)s
:日志消息。%(filename)s
:调用日志记录函数的源文件的文件名。%(pathname)s
:调用日志记录函数的源文件的全路径名。%(module)s
:调用日志记录函数的模块名。%(funcName)s
:调用日志记录函数的函数名。%(lineno)d
:调用日志记录函数的语句所在的行号。%(created)f
:日志事件发生时的时间戳(自UNIX epoch开始的秒数)。%(msecs)d
:日志事件发生时的毫秒部分。%(relativeCreated)d
:日志事件发生时相对于Logger创建时间的毫秒数。%(thread)d
:线程ID。%(threadName)s
:线程名称。%(process)d
:进程ID。%(processName)s
:进程名称。在开发过程中,有时需要捕捉代码中的异常并记录日志。logging
库提供了便捷的方式来实现这一功能。下面是一个示例:
import logging logger = logging.getLogger('my_logger') logger.setLevel(logging.DEBUG) ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) try: 1 / 0 except ZeroDivisionError as e: logger.error('捕捉到异常', exc_info=True)
在上述示例中,捕捉到ZeroDivisionError
异常,并使用logger.error
记录了异常信息。exc_info=True
参数确保异常的堆栈信息也被记录下来。
在logging
库中,每个日志级别都有一个对应的函数,用于记录该级别的日志消息。这些函数包括debug
、info
、warning
、error
和critical
。每个函数的用法基本相同,但它们的日志级别不同,使用时需要根据具体的日志需求选择合适的函数。
logger.debug(msg, *args, **kwargs)
:记录一条DEBUG级别的日志消息。此级别的日志用于详细的信息,通常只在诊断问题时使用。logger.info(msg, *args, **kwargs)
:记录一条INFO级别的日志消息。此级别的日志用于确认程序按预期工作的信息。logger.warning(msg, *args, **kwargs)
:记录一条WARNING级别的日志消息。此级别的日志用于指示某些意外情况或问题,但程序仍可以继续运行。logger.error(msg, *args, **kwargs)
:记录一条ERROR级别的日志消息。此级别的日志用于表示更严重的问题,表明程序不能执行某些功能。logger.critical(msg, *args, **kwargs)
:记录一条CRITICAL级别的日志消息。此级别的日志用于表示严重的错误,表明程序可能无法继续运行。每个日志级别函数都可以接受以下参数:
msg
:这是必需的参数,表示要记录的日志消息。可以是字符串,也可以是包含占位符的字符串,然后通过*args
或**kwargs
传入占位符对应的值。*args
:用于为msg
中的占位符提供参数。这些参数会以位置参数的形式传递,替换msg
中的占位符。**kwargs
:用于为日志消息提供额外的参数,通常用于设置一些可选参数,比如exc_info
、stack_info
、extra
等。exc_info
:用于记录异常信息。如果设置为True
,日志消息会包含异常的堆栈信息。这在记录异常时非常有用。stack_info
:如果设置为True
,日志消息会包含调用日志函数时的堆栈信息。extra
:一个字典,用于向日志消息添加额外的信息。这个字典中的键必须是格式化器中使用的占位符。就是说格式化器中出现的字段,日志捕获函数参数中必须提供,如:
# 创建格式化器
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(user_defined_info)s')
# 定义额外的参数
extra_info = {'user_defined_info': 'xxxxxxx'}
# 记录CRITICAL级别的日志,并使用堆栈信息
logger.critical('这是一个致命错误消息', stack_info=True, extra=extra_info)
如果执行logger.critical函数extra参数中的字典没有提供相应字段的信息,就会报错。
示例代码:
import logging # 创建记录器 logger = logging.getLogger('example_logger') logger.setLevel(logging.DEBUG) # 创建处理器并设置级别 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # 创建格式化器 formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(user_defined_info)s') ch.setFormatter(formatter) # 将处理器添加到记录器 logger.addHandler(ch) # 定义额外的参数 extra_info = {'user_defined_info': 'xxxxxxxxx'} # 记录DEBUG级别的日志 logger.debug('这是一个调试消息', extra=extra_info)
import logging # 创建记录器 logger = logging.getLogger('example_logger') logger.setLevel(logging.DEBUG) # 创建处理器并设置级别 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # 创建格式化器 formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(user)s') ch.setFormatter(formatter) # 将处理器添加到记录器 logger.addHandler(ch) # 定义额外的参数 extra_info = {'user': 'admin'} # 记录DEBUG级别的日志 logger.debug('这是一个调试消息', extra=extra_info) # 记录INFO级别的日志 logger.info('这是一个信息消息', extra=extra_info) # 记录WARNING级别的日志,并使用位置参数 logger.warning('这是一个警告消息,参数1: %s, 参数2: %d', 'param1', 2, extra=extra_info) # 记录ERROR级别的日志,并包含异常信息 try: 1 / 0 except ZeroDivisionError as e: logger.error('捕捉到异常', exc_info=True, extra=extra_info) # 记录CRITICAL级别的日志,并使用堆栈信息 logger.critical('这是一个致命错误消息', stack_info=True, extra=extra_info)
logger.debug(msg, *args, **kwargs)
:记录DEBUG级别的日志消息。此级别的日志用于详细的信息,通常只在诊断问题时使用。示例:
logger.debug('这是一个调试消息', extra=extra_info)
logger.info(msg, *args, **kwargs)
:记录INFO级别的日志消息。此级别的日志用于确认程序按预期工作的信息。示例:
logger.info('这是一个信息消息', extra=extra_info)
logger.warning(msg, *args, **kwargs)
:记录WARNING级别的日志消息。此级别的日志用于指示某些意外情况或问题,但程序仍可以继续运行。示例:
logger.warning('这是一个警告消息,参数1: %s, 参数2: %d', 'param1', 2, extra=extra_info)
logger.error(msg, *args, **kwargs)
:记录ERROR级别的日志消息。此级别的日志用于表示更严重的问题,表明程序不能执行某些功能。示例:
try:
1 / 0
except ZeroDivisionError as e:
logger.error('捕捉到异常', exc_info=True, extra=extra_info)
logger.critical(msg, *args, **kwargs)
:记录CRITICAL级别的日志消息。此级别的日志用于表示严重的错误,表明程序可能无法继续运行。示例:
logger.critical('这是一个致命错误消息', stack_info=True, extra=extra_info)
f-string
,建议使用%惰性格式化
参考文章:python fstring教程(f-string教程)(python3.6+格式化字符串方法)(%惰性格式化%)
根据你提供的需求和配置,我将给出一个示例代码,展示如何为一个大型的在线电商平台配置一个复杂的日志系统,包括不同级别和多个输出目标的处理器(Handlers)。
import logging import sys def setup_logging(): # 创建一个名为 'ecommerce' 的全局 Logger logger_ = logging.getLogger('ecommerce') logger_.setLevel(logging.DEBUG) # 设置 Logger 的级别为 DEBUG # 创建文件 Handler 1:用于操作日志,记录级别为 INFO file_handler_info = logging.FileHandler('operations.log') file_handler_info.setLevel(logging.INFO) file_handler_info.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) # 创建文件 Handler 2:用于错误报告,记录级别为 ERROR file_handler_error = logging.FileHandler('errors.log') file_handler_error.setLevel(logging.ERROR) file_handler_error.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) # 创建控制台 Handler:用于调试信息,记录级别为 DEBUG console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) # 创建性能监控 Handler:用于性能监控日志,记录级别为 INFO performance_handler = logging.FileHandler('performance.log') performance_handler.setLevel(logging.INFO) performance_handler.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) # 将所有 Handler 添加到 Logger logger_.addHandler(file_handler_info) logger_.addHandler(file_handler_error) logger_.addHandler(console_handler) logger_.addHandler(performance_handler) return logger_ # 配置并获取 Logger logger = setup_logging() # 使用 Logger 记录一些示例消息 logger.debug('Debug message: User session started') logger.info('Info message: User logged in') logger.warning('Warning message: User session timeout is approaching') logger.error('Error message: Failed to process transaction') logger.critical('Critical message: System is down')
ecommerce
,它有四个不同的 Handler,每个 Handler 都设置了不同的日志级别和输出目标。这样的配置确保日志信息根据其重要性和用途被适当地分流和存储,从而提高了系统的可维护性和监控能力。
代码中名为 ecommerce
的全局 Logger(记录器),设置级别为 DEBUG
,这意味着DEBUG
级别及以上的日志消息会被记录;
file_handler_info
处理器设置级别为INFO
,意味着会处理INFO
级别及以上的日志消息;
同理,其他处理器也会处理其设置的级别及以上的日志消息;
下面是一个使用过滤器来区分日志内容的示例:
import logging import sys class PerformanceFilter(logging.Filter): def filter(self, record): # 打印调试信息以确认过滤器是否被触发 contains_performance = 'Performance' in record.getMessage() print(f"Filtering: {record.getMessage()}, Result: {contains_performance}") return contains_performance def setup_logging(): logger = logging.getLogger('ecommerce') logger.setLevel(logging.DEBUG) file_handler_info = logging.FileHandler('operations.log') file_handler_info.setLevel(logging.DEBUG) file_handler_info.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) performance_handler = logging.FileHandler('performance.log') performance_handler.setLevel(logging.DEBUG) performance_handler.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s')) performance_handler.addFilter( PerformanceFilter()) # 只记录包含'performance'的日志消息 logger.addHandler(file_handler_info) logger.addHandler(console_handler) logger.addHandler(performance_handler) return logger logger = setup_logging() logger.debug('User logged in') # 不会记录到 performance.log logger.debug('Performance metrics: CPU usage at 70%') # 会记录到 performance.log
在这个例子中,PerformanceFilter
过滤器将检查日志消息是否包含 ‘Performance’ 这个关键字,如果包含,则这些日志消息会被记录到 performance.log
文件中,而不是 operations.log
。这样可以有效地将日志内容区分开来,确保每个文件只包含相关的信息。
注意:python in语法判断字符串中是否包含字串是匹配大小写的。
在实际应用中,日志系统通常会根据应用的需求进行配置和使用。下面是一个更复杂的示例,展示了如何在实际应用中配置和使用logging
库:
import logging import logging.config log_config = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'detailed': { 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(pathname)s:%(lineno)d', }, 'simple': { 'format': '%(asctime)s - %(levelname)s - %(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'simple', 'level': logging.DEBUG, }, 'file': { 'class': 'logging.FileHandler', 'filename': 'app.log', 'formatter': 'detailed', 'level': logging.DEBUG, }, 'error_file': { 'class': 'logging.FileHandler', 'filename': 'errors.log', 'formatter': 'detailed', 'level': logging.ERROR, }, }, 'loggers': { '': { 'handlers': ['console', 'file'], 'level': logging.DEBUG, 'propagate': True, }, 'error_logger': { 'handlers': ['error_file'], 'level': logging.ERROR, 'propagate': False, }, } } logging.config.dictConfig(log_config) logger = logging.getLogger('app_logger') error_logger = logging.getLogger('error_logger') logger.debug('这是一个调试消息') logger.info('这是一个信息消息') logger.warning('这是一个警告消息') logger.error('这是一个错误消息') logger.critical('这是一个致命错误消息') try: 1 / 0 except ZeroDivisionError as e: error_logger.error('捕捉到异常', exc_info=True)
在这个示例中,配置了多个格式化器、处理器和记录器。普通日志消息输出到控制台和app.log
文件,而错误日志消息还会额外输出到errors.log
文件。这样,可以在实际应用中根据不同的需求记录和管理日志。
本文详细介绍了Python的logging
库,从基本概念、基本用法到高级配置和实际应用。通过logging
库,开发者可以灵活地记录和管理日志,帮助调试和监控应用程序。掌握logging
库的使用方法,是每个Python开发者必备的技能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。