Python 的 logging
库是 Python 标准库中用于记录日志的模块,它提供了一种灵活且强大的方式来记录应用程序的日志信息。
1. 基本概念
在使用 logging
库之前,需要了解一些基本概念:
-
Logger:Logger 对象是应用程序中日志的入口点。每个 Logger 都有一个名称,可以通过名字获取 Logger 实例。
-
Handler:Handler 负责将日志记录发送到目的地,如控制台、文件等。
-
Formatter:Formatter 定义了日志记录的输出格式。
-
Filter:Filter 可以用于过滤日志记录,决定是否处理某条日志。
-
Level:日志级别,用于表示日志的严重程度,如 DEBUG、INFO、WARNING、ERROR、CRITICAL 等。
2. 基本用法
以下是一个简单的示例,演示如何使用 logging
库记录日志:
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 创建 Logger
logger = logging.getLogger('example_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')
在这个示例中:
-
使用
basicConfig
方法配置日志的基本设置,包括日志级别、日志文件名、文件模式和日志格式。 -
创建一个名为 ‘example_logger’ 的 Logger 实例。
-
使用不同级别的日志方法记录日志信息。
3. 配置日志文件
要将日志记录到文件中,可以使用 FileHandler
处理器。以下是一个更详细的配置示例:
import logging
# 创建 Logger
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)
# 创建 FileHandler,将日志写入文件
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 创建 Formatter,定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
# 将 FileHandler 添加到 Logger
logger.addHandler(file_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 实例,并设置其日志级别为 DEBUG。
-
创建了一个 FileHandler,指定日志文件名为 ‘app.log’,并设置其日志级别为 DEBUG。
-
创建了一个 Formatter,定义了日志的输出格式,并将其设置给 FileHandler。
-
将 FileHandler 添加到 Logger 中。
-
使用不同的日志级别记录日志信息,这些日志会被写入到 ‘app.log’ 文件中。
4. 配置控制台输出
除了文件输出,还可以配置日志输出到控制台。以下是一个示例:
import logging
# 创建 Logger
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)
# 创建 FileHandler,将日志写入文件
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 创建 Formatter,定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
# 创建 StreamHandler,将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# 将 Handler 添加到 Logger
logger.addHandler(file_handler)
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 将日志写入文件。
-
创建了一个 StreamHandler 将日志输出到控制台,并设置其日志级别为 INFO。
-
将两个 Handler 都添加到 Logger 中。
-
这样,日志会同时输出到文件和控制台,且控制台只显示 INFO 及以上级别的日志。
5. 高级配置
logging
库还支持更高级的配置方式,如使用字典配置、配置文件等。
5.1 使用字典配置
可以使用 dictConfig
方法通过字典来配置日志系统。以下是一个示例:
import logging
import logging.config
logging_config = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
},
'handlers': {
'file': {
'class': 'logging.FileHandler',
'filename': 'app.log',
'level': 'DEBUG',
'formatter': 'standard'
},
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'standard'
}
},
'loggers': {
'example_logger': {
'level': 'DEBUG',
'handlers': ['file', 'console'],
'propagate': False
}
},
'root': {
'level': 'WARNING',
'handlers': ['console']
}
}
logging.config.dictConfig(logging_config)
logger = logging.getLogger('example_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')
在这个示例中:
-
使用字典定义了日志配置,包括格式化器、处理器和日志器。
-
通过
dictConfig
方法加载配置。 -
创建了一个名为 ‘example_logger’ 的 Logger,配置了文件和控制台处理器。
-
同时配置了根日志器,将 WARNING 及以上级别的日志输出到控制台。
5.2 使用配置文件
可以将日志配置保存在单独的配置文件中,然后通过 fileConfig
方法加载配置。以下是一个示例:
logging.config
[loggers]
keys=root,example_logger
[handlers]
keys=consoleHandler,fileHandler
[formatters]
keys=standardFormatter
[logger_root]
level=WARNING
handlers=consoleHandler
[logger_example_logger]
level=DEBUG
handlers=fileHandler,consoleHandler
qualname=example_logger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=standardFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=standardFormatter
args=('app.log',)
[formatter_standardFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
Python代码
import logging
import logging.config
logging.config.fileConfig('logging.config')
logger = logging.getLogger('example_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')
在这个示例中:
-
创建了一个配置文件
logging.config
,定义了日志器、处理器和格式化器。 -
通过
fileConfig
方法加载配置文件。 -
创建了一个名为 ‘example_logger’ 的 Logger,配置了文件和控制台处理器。
6. 日志级别
日志级别用于表示日志的严重程度,常见的日志级别如下:
-
NOTSET:0,未设置,不记录任何日志。
-
DEBUG:10,详细信息,用于调试。
-
INFO:20,确认事物按预期工作。
-
WARNING:30,指示可能的问题,但不影响当前操作。
-
ERROR:40,由于较严重的问题,功能无法执行。
-
CRITICAL:50,严重错误,表明程序可能无法继续运行。
日志级别从低到高依次是 DEBUG < INFO < WARNING < ERROR < CRITICAL。
7. 日志记录器的继承
在 logging
库中,日志记录器支持继承。可以创建父日志记录器和子日志记录器,子记录器可以继承父记录器的设置。
例如:
import logging
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'standard'
}
},
'loggers': {
'parent_logger': {
'level': 'DEBUG',
'handlers': ['console'],
'propagate': True
},
'parent_logger.child_logger': {
'level': 'INFO',
'handlers': ['console'],
'propagate': True
}
}
})
parent_logger = logging.getLogger('parent_logger')
child_logger = logging.getLogger('parent_logger.child_logger')
parent_logger.debug('Parent debug message')
child_logger.debug('Child debug message')
在这个示例中:
-
创建了一个父日志记录器 ‘parent_logger’ 和一个子日志记录器 ‘parent_logger.child_logger’。
-
子记录器继承了父记录器的设置,但可以覆盖自己的设置。
-
propagate
属性决定是否将日志消息传递给父记录器。
8. 日志的过滤
可以使用 Filter 对象来过滤日志记录,决定是否处理某条日志。以下是一个示例:
import logging
class InfoFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.INFO
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.addFilter(InfoFilter())
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
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')
在这个示例中:
-
创建了一个自定义的 Filter 类
InfoFilter
,只允许 INFO 级别的日志通过。 -
将这个 Filter 添加到 StreamHandler 中。
-
因此,只有 INFO 级别的日志会输出到控制台。
9. 多进程日志记录
在多进程环境中,需要特别注意日志记录,以避免日志文件的竞态条件。可以使用 logging.handlers.QueueHandler
和 logging.handlers.QueueListener
来解决这个问题。
以下是一个示例:
logger_config.py
import logging
import logging.handlers
import multiprocessing
def configure_logger():
queue = multiprocessing.Manager().Queue(-1)
queue_handler = logging.handlers.QueueHandler(queue)
queue_listener = logging.handlers.QueueListener(queue, *logging.root.handlers)
queue_listener.start()
return queue_handler, queue_listener
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w',
format='%(asctime)s - %(processName)s - %(levelname)s - %(message)s')
queue_handler, queue_listener = configure_logger()
logger = logging.getLogger('example_logger')
logger.addHandler(queue_handler)
logger.setLevel(logging.DEBUG)
logger.debug('This is a debug message from the main process')
worker.py
import logging
import logging.handlers
import multiprocessing
def worker(logger):
logger.debug('This is a debug message from a worker process')
if __name__ == "__main__":
logging.config.fileConfig('logging.config')
logger = logging.getLogger('example_logger')
processes = []
for _ in range(5):
p = multiprocessing.Process(target=worker, args=(logger,))
processes.append(p)
p.start()
for p in processes:
p.join()
在这个示例中:
-
在主进程中配置日志记录,并创建一个队列处理器和队列监听器。
-
在 worker 进程中,使用配置好的日志记录器记录日志。
-
通过队列处理器和队列监听器,确保日志消息能够正确地从 worker 进程传送到主进程的日志处理系统中。
10. 异常处理
在记录日志时,尤其是记录异常信息时,可以使用 exception
方法,它会自动捕获异常并记录 traceback 信息。
import logging
logger = logging.getLogger('example_logger')
logger.setLevel(logging.ERROR)
try:
x = 1 / 0
except ZeroDivisionError:
logger.exception('Division by zero error')
在这个示例中:
- 当捕获到
ZeroDivisionError
异常时,使用logger.exception
方法记录错误信息,同时会记录异常的 traceback 信息。
11. 日志轮转
在生产环境中,日志文件可能会变得非常大,因此需要日志轮转机制来管理日志文件的大小和数量。可以使用 logging.handlers.RotatingFileHandler
或 logging.handlers.TimedRotatingFileHandler
来实现日志轮转。
11.1 RotatingFileHandler
按文件大小轮转:
import logging
import logging.handlers
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler('app.log', maxBytes=1024*1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
for _ in range(10000):
logger.debug('This is a debug message')
在这个示例中:
- 使用
RotatingFileHandler
,当文件大小达到 1MB 时,会创建一个新的日志文件,最多保留5个备份文件。
11.2 TimedRotatingFileHandler
按时间轮转:
import logging
import logging.handlers
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)
handler = logging.handlers.TimedRotatingFileHandler('app.log', when='midnight', interval=1, backupCount=7)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.debug('This is a debug message')
在这个示例中:
- 使用
TimedRotatingFileHandler
,每天 midnight 时创建一个新的日志文件,保留7天的备份文件。