日志主要目的是在程序运行过程中,输出我们感兴趣的内容,以便程序出现异常时,我们根据日志排查问题。Python中内置的日志模块是logging。
日志输出的内容是分等级的,我们可以根据等级控制输出的内容。可以通过help(loggging)查看日志帮助文档,文档最下方列出了Python支持的日志级别。
DATA
BASIC_FORMAT = ‘%(levelname)s:%(name)s:%(message)s’
CRITICAL = 50
DEBUG = 10
ERROR = 40
FATAL = 50
INFO = 20
NOTSET = 0
WARN = 30
WARNING = 30
日志的等级是分高低的,从低到高分别是:NOTSET DEBUG INFO WARNING ERROR CRITICAL。NOTSET不太常用。
DEBUG:详细的信息,通常只出现在诊断问题上
INFO:确认一切按预期运行
WARNING:一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”)。这个软件还能按预期工作。
ERROR:更严重的问题,软件没能执行一些功能
CRITICAL:一个严重的错误,这表明程序本身可能无法继续运行
这5个等级,也分别对应5种打日志的方法: debug 、info 、warning 、error 、critical。默认的是WARNING。
日志的输出规则是,输出大于等于设置的日志级别的日志信息。比如设置等级是DEBUG,那么大于等于10的级别的日志信息都会输出来。
# coding=utf-8
__author__ = 'liu.chunming'
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# use logging
logging.debug('this is a loggging debug message')
logging.info('this is a loggging info message')
logging.warning('this is loggging a warning message')
logging.error('this is an loggging error message')
logging.critical('this is a loggging critical message')
通过logging.basicConfig函数对日志的输出格式及方式做相关配置,上面代码设置日志的输出等级是WARNING级别,意思是WARNING级别以上的日志才会输出。另外还制定了日志输出的格式。
通过format定义日志格式,通常我是用的日志格式就像上面那样:
%(asctime)s %(filename)s[%(lineno)d]: %(levelname)s %(message)s
通过这个日志格式打印出来的日志效果是:
2020-05-30 12:50:47,273 - learn_str.py[line:10] - INFO: this is a loggging info message
2020-05-30 12:50:47,274 - learn_str.py[line:11] - WARNING: this is loggging a warning message
2020-05-30 12:50:47,274 - learn_str.py[line:12] - ERROR: this is an loggging error message
2020-05-30 12:50:47,274 - learn_str.py[line:13] - CRITICAL: this is a loggging critical message
前面输出日志的方法,是将日志输出到控制台了。但是实际工程中,往往是输出到文件中。优点在于,它将配置信息和代码进行了分离,这一方面降低了日志的维护成本,同时还使得非开发人员也能够去很容易地修改日志配置。
在项目目录下config文件夹中,创建一个日志配置文件logger_config.ini。内容如下:
[loggers]
;声明多个日志器logger,root是必须的,也可定义多个,比如这里的simpleExample,必须在下面的[logger_]部分定义
keys=root,simpleExample
[handlers]
;声明了三个处理器handlers,必须在下面[handler_]中定义。
keys=rotatingFileHandler,streamHandler,errorHandler
[formatters]
;声明格式器formatter
keys=simpleFmt
[logger_root]
;handlers必须取自handlers这个section,并且
level=DEBUG
handlers=rotatingFileHandler,streamHandler,errorHandler
[logger_simpleExample]
;非root的logger,必须定义qualname
level=DEBUG
handlers=streamHandler
qualname=simpleExample
propagate=0
;定义日志格式
[formatter_simpleFmt]
format=%(asctime)s %(filename)s[%(lineno)d]: %(levelname)s %(message)s
;定义rotatingFileHandler,输出
[handler_rotatingFileHandler]
class=handlers.TimedRotatingFileHandler
;每一天午夜12点将当天的日志转存到一份新的日志文件中,并且加上时间戳后缀,最多保存6个文件,编码格式UTF-8,支持中文。
args=(os.path.abspath(os.getcwd() + "/logs/default.log"),"midnight", 1, 6,'utf-8')
level=INFO
formatter=simpleFmt
;定义errorHandler,输出错误日志到到error.log
[handler_errorHandler]
class=handlers.TimedRotatingFileHandler
args=(os.path.abspath(os.getcwd() + "/logs/error.log"), "midnight", 1, 6,'utf-8')
level=ERROR
formatter=simpleFmt
;定义streamHandler,输出日志到控制台
[handler_streamHandler]
class=StreamHandler
args=(sys.stdout,)
level=INFO
formatter=simpleFmt
配置文件中一定要包含loggers
、handlers
、formatters
这些section。它们通过keys
这个option来指定该配置文件中需要定义的loggers、handlers和formatters,多个值之间用逗号分隔;另外loggers
这个section中的keys一定要包含root这个值;
seciton的命名规则为[logger_loggerName]
、[formatter_formatterName]
、[handler_handlerName]
。
logger的section必须指定level
和handlers
这两个option,level
的可取值为DEBUG
、INFO
、WARNING
、ERROR
、CRITICAL
,handlers
的值是以逗号分隔的handler名字列表,这里出现的handler必须出现在[handlers]这个section中,并且相应的handler必须在配置文件中有对应的section定义;
对于非root logger来说,除了level
和handlers
这两个option之外,还需要一些额外的option,其中qualname
是必须提供的option,它表示在logger层级中的名字,在应用代码中通过这个名字得到logger;propagate
是可选项,其默认是为1,表示消息将会传递给高层次logger的handler,通常我们需要指定其值为0。
formatter的sectioin中,所有option都是可选的,但是一般都会包括format
option用于指定日志格式。
handler的section中,必须指定class
和args
这两个option,level
和formatter
是可选的,class
表示用于创建handler的类名,args
表示传递给class
所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;level
与logger中的level一样,而formatter
指定的是该处理器所使用的格式器,来自formatters这个sections。
上面的例子中,使用的FileHandler是handlers.TimedRotatingFileHandler
,可以按照时间滚动转存日志,避免一个日志文件变的非常大。另外一个支持滚动转存的日志Handler是RotatingFileHandler
,根据文件大小转存。内置的RotatingFileHandler
和TimedRotatingFileHandler
对于多线程是安全的,但是不支持多进程。多进程的时候可以使用ConcurrentLogHandler(需要自行安装)按照文件大小轮转。
logger.py
在项目录下的utils目录里面,创建一个logger.py文件,通过logging.config.fileConfig
读取日志配置文件logger_config.ini,并且通过logging.getLogger
获取全局logger对象。
### import logging
import logging.config
import os
def get_logger(name='root'): # 获取名字叫做root的logger配置
conf_log = os.path.abspath(os.path.dirname(os.path.dirname(__file__)) + "/config/logger_config.ini")
logging.config.fileConfig(conf_log) # 读取配置文件
return logging.getLogger(name) # 获取logger对象
log = get_logger(__name__) # 获取全局logger对象
from utils.logger import log
log.info("start")
try:
print(1 / 0)
except Exception as e:
log.exception(e)
log.debug("end")