1-Python之logging模块
什么是日志
日志是对软件执行时所发生事件的一种追踪方式。软件开发人员对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述(比如:每个事件发生时的数据都是不同的)。开发者还会区分事件的重要性,重要性也被称为等级
或严重性
。
什么时候使用日志
日志(logging)模块提供了一系列的函数(debug、info、warning、critical)来适应不同的应用场景。想要决定何时使用日志,请看下表,其中显示了对于每个通用任务集合来说最好的工具。
想要执行的任务 | 此任务最好的工具 |
---|---|
对于命令行或程序的应用,结果显示在控制台 | print() |
在对程序的普通操作发生时提交事件报告(比如:状态监控和错误调查) | logging.info() 函数(当有诊断目的需要详细输出信息时使用 logging.debug() 函数) |
提出一个警告信息基于一个特殊的运行时事件 | warnings.warn() 位于代码库中,该事件是可以避免的,需要修改客户端应用以消除告警。logging.warning() 不需要修改客户端应用,但是该事件还是需要引起关注 |
对一个特殊的运行时事件报告错误 | 引发异常 |
报告错误而不引发异常(如在长时间运行中的服务端进程的错误处理) | logging.error(), logging.exception() 或 logging.critical() 分别适用于特定的错误及应用领域 |
下表展示了logging
中,日志功能应以所追踪事件级别或严重性而定。各级别适用性如下(以严重性递减):
等级 | 数值 | 描述 |
---|---|---|
CRITICAL | 50 | 严重的错误,表明程序已经不能继续执行 |
FATAL(不推荐) | 50 | FATAL是CRITICAL的别名。 |
ERROR | 40 | 由于严重的问题,程序的某些功能已经不能正常执行 |
WARNING | 30 | 表明已经或即将发生的意外(例如磁盘空间不足)。但程序仍按照预期运行 |
WARN(不推荐) | 30 | WARN是WANING的简写形式。 |
INFO | 20 | 确认程序按预期运行 |
DEBUG | 10 | 细节信息,在我们调试程序是使用 |
NOTSET | 0 | 不设置 |
默认的级别是WARNING
,意味着只会追踪该级别及以上的事件,除非更改日志配置。
basicConfig
一个简单的示例,将结果输出到控制台:
import logging
logging.warning('info level') # WARNING:root:info levelb
将结果输出到文件:
import logging
logging.basicConfig(filename='test.log', level=20)
logging.log(10, '级别为10的一条日志')
logging.log(20, '级别为20的一条日志')
上例中,通过basicConfig
方法将日志输出到test.log
文件,并且设置只有级别大于等于20的才会写入到该日志文件。也就是说,上例中,第一条级别为10的日志将不会写入到文件。并且,需要注意的是,如果你查看日志文件,如果出现乱码的话,请检查编码方式。
上例中,在basicConfig
方法中,级别20也可以这样指定:
logging.basicConfig(filename='test.log', level=logging.INFO)
logging.debug('debug level: 10')
logging.info('info level: 20')
logging.warning('warning level: 30')
logging.error('error level: 40')
logging.critical('critical level: 50')
上例中只有级别大于等于20的将会被写入文件。logging.INFO
其实代表的就是20,这是Python在源码中帮我们指定了:
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
接下来,我们来看看basicConfig
方法都可以指定哪些参数:
- filename,即日志输出的文件名,如果指定了这个信息之后,实际上会启用``FileHandler
,而不再是
StreamHandler`,这样日志信息便会输出到文件中了。 - filemode,日志文件写入方式,可以是
w
和a
,默认的是a
模式。 - format,指定日志信息的输出格式,详细参考,这里列出常用的参数:
- %(levelno)s:打印日志级别的数值。
- %(levelname)s:打印日志级别的名称。
- %(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]。
- %(filename)s:打印当前执行程序名。
- %(funcName)s:打印日志的当前函数。
- %(lineno)d:打印日志的当前行号。
- %(asctime)s:打印日志的时间。
- %(thread)d:打印线程ID。
- %(threadName)s:打印线程名称。
- %(process)d:打印进程ID。
- %(processName)s:打印线程名称。
- %(module)s:打印模块名称。
- %(message)s:打印日志信息。
- datefmt,指定时间的输出格式。
- style,如果format指定,该参数可以指定格式化时的占位符。例如
'{'
或'$'
用于printf风格,str.format()
或string.Template
分别。默认为'%'
。3.2版本新增参数。 - level,指定日志输出的类别,程序会输出大于等于此级别的信息。
- stream,在没有指定``filename
的时候会默认使用
StreamHandler,这时
stream`可以指定初始化的文件流。 - handlers:可以指定日志处理时所使用的 Handlers,必须是可迭代的。
来个示例:
import logging
logging.basicConfig(
filename='test.log',
filemode='w',
level=logging.DEBUG,
datefmt='%Y/%m/%d %H:%M:%S',
format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s'
)
logging.debug('debug level: 10')
logging.info('info level: 20')
logging.warning('warning level: 30')
logging.error('error level: 40')
logging.critical('critical level: 50')
输出结果如下:
2019/05/30 14:38:09 - root - DEBUG - 36 - 日志模块 - debug level: 10
2019/05/30 14:38:09 - root - INFO - 37 - 日志模块 - info level: 20
2019/05/30 14:38:09 - root - WARNING - 38 - 日志模块 - warning level: 30
2019/05/30 14:38:09 - root - ERROR - 39 - 日志模块 - error level: 40
2019/05/30 14:38:09 - root - CRITICAL - 40 - 日志模块 - critical level: 50
需要注意的是,logging.basicConfig
只生效一次,比如:
import logging
logging.basicConfig(
filename='test1.log',
filemode='w',
level=logging.DEBUG
)
# 无效
logging.basicConfig(
filename='test2.log',
filemode='a',
level=logging.INFO
)
logging.debug('debug level: 10')
logging.info('info level: 20')
logging.warning('warning level: 30')
logging.error('error level: 40')
logging.critical('critical level: 50')
正如上例所示,我们配置了两次basicConfig
。但如果运行你会发现,只有第一个配置生效了,第二个配置不会生效。原因是当在第一次配置的时候,logging
在内部就会进行配置,第二次再次配置的时候,logging
就会认为我已经配置好了,不需要再次配置了。
handler
接下来,我们来看handler
的用法:
import logging
# 日志输出到哪?
file_handler = logging.FileHandler(filename='test.log', mode='w', encoding='utf-8')
# 以什么格式写
fmt = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
# 为日志文件指定写入格式
file_handler.setFormatter(fmt=fmt)
# 谁来写?日志的级别是什么?
logger = logging.Logger(name='logger', level=logging.INFO) # name:日志对象的名称,可以自定义
# 往哪写?
logger.addHandler(file_handler)
# 写什么?
logger.debug('debug level: 10')
logger.info('info level: 20')
logger.warning('warning level: 30')
logger.error('error level: 40')
logger.critical('critical level: 50')
如上例所示,这里我们一步步的对日志进行配置,然后最后写入到test.log
文件。结果如下:
2019-05-30 15:32:07,118 - logger - INFO - 82 - 日志模块 - info level: 20
2019-05-30 15:32:07,118 - logger - WARNING - 83 - 日志模块 - warning level: 30
2019-05-30 15:32:07,118 - logger - ERROR - 84 - 日志模块 - error level: 40
2019-05-30 15:32:07,119 - logger - CRITICAL - 85 - 日志模块 - critical level: 50
除此之外,我们还可以使用其他的Handler
进行日志输出,logging
模块提供的Handler
有:
- StreamHandler:logging.StreamHandler;日志输出到流,可以是 sys.stderr,sys.stdout 或者文件。
- FileHandler:logging.FileHandler;日志输出到文件。
- BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式。
- RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚。
- TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件。
- SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets。
- DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets。
- SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址。
- SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog。
- NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志。
- MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer。
- HTTPHandler:logging.handlers.HTTPHandler;通过”GET”或者”POST”远程输出到HTTP服务器。
再来一个示例,我们使用不同的Handler
实现日志同时输出到控制台、文件、HTTP服务器:
import sys
import logging
from logging.handlers import HTTPHandler
# 创建日志对象
logger = logging.getLogger('logger')
# 将日志输出到文件 FileHandler
file_handler = logging.FileHandler(filename='test.log', mode='w', encoding='utf-8')
file_fmt = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
file_handler.setFormatter(file_fmt)
file_handler.setLevel(level=logging.INFO)
logger.addHandler(file_handler)
# 将日志输出到控制台 StreamHandler
stream_handler = logging.StreamHandler(sys.stdout)
stream_fmt = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
stream_handler.setFormatter(stream_fmt)
stream_handler.setLevel(level=logging.DEBUG)
logger.addHandler(stream_handler)
# 将日志输出到HTTP服务器
http_handler = HTTPHandler(host='localhost:8888', url='/', method='GET')
http_handler.setLevel(level=logging.INFO)
logger.addHandler(http_handler)
# 写什么
logger.debug(msg='debug level: 10')
logger.info(msg='info level: 20')
logger.warning(msg='warning level: 30')
logger.error(msg='error level: 40')
logger.critical(msg='critical level: 50')
上述代码会将日志分别输入到test.log
文件、控制台和HTTP
服务器,当然,在执行这段代码前,还需要启动HTTPServer
,并运行在8888端口。url为根路径。在Python2.x中,提供了一种简单的HTTPServer。使用如下图所示。
Traceback
除了应用之外,logging
模块还支持错误回溯,也就是Traceback
功能:
import logging
logger = logging.getLogger('logger')
logger.setLevel(level=logging.DEBUG)
try:
result = 3 / 0 # O不能当被除数
except Exception as e:
logger.error(e, exc_info=True)
上例中,如果exc_info
参数为False
的话,仅打印报错信息:
division by zero
现在将exc_info
参数设置为True
,就会得到完整的Traceback
信息:
division by zero
Traceback (most recent call last):
File "M:/日志模块.py", line 134, in <module>
result = 3 / 0 # O不能当被除数
ZeroDivisionError: division by zero
文件配置
logging
模块还支持将配置写入到yaml
中,方便在任何地方调用。
现在,我们编写customLog.yaml
中的代码:
version: 1
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handler:
console:
class: logging.StreamHandler
level: INFO
formatter: simple
stream: ext://sys.stdout
loggers:
simpleExample:
level: INFO
handlers: [console]
propagate: no
root:
level: INFO
handlers: [console]
我们在实际中只需要读取这个yaml
文件即可:
import os
import logging
from logging import config
import yaml # pip install pypaml
def custom_configure(file_ath, default_level=logging.INFO):
if os.path.exists(file_ath):
with open(file_ath, 'r', encoding='utf8') as f:
config.dictConfig(yaml.load(f))
else:
logging.basicConfig(level=default_level)
def run():
logging.debug(msg='debug level: 10')
logging.info(msg='info level: 20')
logging.warning(msg='warning level: 30')
logging.error(msg='error level: 40')
logging.critical(msg='critical level: 50')
if __name__ == '__main__':
custom_configure('log.yaml')
run()
结果展示如下:
2019-05-30 19:50:03,868 - root - INFO - info level: 20
2019-05-30 19:50:03,868 - root - WARNING - warning level: 30
2019-05-30 19:50:03,868 - root - ERROR - error level: 40
2019-05-30 19:50:03,868 - root - CRITICAL - critical level: 50
日志切割
logging
支持日志切割,什么意思呢?就是每隔多少时间,生成一个日志,或者根据文件大小分割日志,这在一些情况下相当有用,先来看根据时间来分割日志。
根据日期时间分割日志
logging
还支持日志切割,什么意思呢?就是每隔多少时间,生成一个日志,这在一些情况下相当有用,来看代码:
import sys
import time
import logging
from logging import handlers
def logger_configure(*args, fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y/%m/%d %H:%M:%S',
level=logging.INFO
):
logging.basicConfig(
format=fmt,
datefmt=datefmt,
level=level,
handlers=list(args)
)
if __name__ == '__main__':
file_handler = handlers.TimedRotatingFileHandler(filename='x1.log', when='S', interval=5, backupCount=0, encoding='utf8')
stream_handler = logging.StreamHandler(stream=sys.stdout)
logger_configure(file_handler, stream_handler)
for i in range(1, 20):
time.sleep(1)
logging.info('info level: {}'.format(i))
上例中,日志切割通过TimedRotatingFileHandler
方法完成,相关参数:
- filename,日志文件名。
- when,指定切割时间,可选参数有:
- S,秒。
- M,分。
- H,时。
- D,天。
- midnight,每天凌晨。
- W{0-6},每周,记住不是从1-7,而是从0开始的。
- interval,每隔when时间切割一次。
- backupCount,该参数如果大于0,则日志切割时,只保留指定的日志文件数量。什么意思呢?比如如上例中指定0,那么每5秒自动创建一个新的日志文件(文件名的格式是
x1.log.2019-05-31_10-12-57
),保存这5秒内的日志记录,再过5秒再次创建一个新的日志文件。这些创建的文件都会保存。但如果backupCount
参数如果设置为指定的数量,比如设置为2,那么它只会保留最新的两个时间点的日志文件,之前的文件都将被删除。下图是当backupCount
参数设置为2时,在本地生的日志文件。
根据文件大小分割日志
http://www.360doc.com/content/15/0125/23/10072361_443701619.shtml
推荐配置
通过之前日志的不同应用,我们可以总结出来一份拿走就用的配置:
import sys
import logging
def logger_configure(*args, fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y/%m/%d %H:%M:%S',
level=logging.INFO
):
logging.basicConfig(
format=fmt,
datefmt=datefmt,
level=level,
handlers=list(args)
)
if __name__ == '__main__':
file_handler = logging.FileHandler('x1.log', mode='a', encoding='utf8')
stream_handler = logging.StreamHandler(stream=sys.stdout)
logger_configure(file_handler, stream_handler)
logging.info('info level: 20')
上例中,我们将logging
的配置封装成函数,给一些默认的配置参数,也可以手动配置。而args
则接收多个输出模式。当我们在使用的时候直接调用该函数,并传递输出模式即可。
示例
import logging
class LoggerHandler:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(LoggerHandler, cls).__new__(cls)
return cls._instance
def __init__(self, log_name, log_level, file_path, stream_level='info', file_level='warning'):
self.logger_name = log_name
self.logger_level = log_level
self.file_path = file_path
self.stream_level = stream_level
self.file_level = file_level
# 创建日志对象
self.logger = logging.getLogger(self.logger_name)
# 设置默认日志级别
self.logger.setLevel(self.logger_level)
# 设置日志输出流
to_stream = logging.StreamHandler()
to_file = logging.FileHandler(self.file_path)
# 设置日志输出级别
to_stream.setLevel(self.stream_level)
to_file.setLevel(self.file_level)
# 设置日志输出格式
formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
to_stream.setFormatter(formatter)
to_file.setFormatter(formatter)
self.logger.addHandler(to_stream)
self.logger.addHandler(to_file)
log = LoggerHandler(
log_name='log',
log_level=logging.INFO,
file_path='./log.log',
stream_level=logging.INFO,
file_level=logging.WARNING
)
log.logger.info('info')
log.logger.error('error')
log.logger.warning('warning')
Django中配置系统日志
python3.9 + django3.2
Django中的日志配置,也是使用Python内置的logging模块完成的。
django官方日志配置文档:https://docs.djangoproject.com/zh-hans/3.2/topics/logging/
日志信息从严重程度由高到低,一共分了5个等级。
由loging模块默认提供了5个操作方法,分别可以记录以下5个等级日志的。
CRITICAL(fatal): 致命错误,程序根本跑不起来。
ERROR: 运行错误,程序运行发生错误的地方时就会退出程序。
WARNING: 运行警告,程序运行发生警告的地方时会显示警告提示,但是程序会继续往下执行。
INFO: 运行提示,一般的系统信息,并非日志
DEBUG: 调试信息,排查故障时使用的低级别系统信息
来看怎么配置吧!
1. 在settings.py中追加如下配置
see also:
日志HOWTO — Python 3.7.3 文档 | [Python中logging模块的基本用法](https://cuiqingcai.com/6080.html) | logging.config --- 日志记录配置 | python(logging )日志模块学习