一、日志模块logging
在一个程序运行过程中,会产生很多状态信息,有正常执行程序输出的返回值、有报错等等,这些统称叫日志(log)信息!比如程序出现故障导致崩溃等诸多问题,这时候我们就需要当时程序的状态信息来定位问题,这是日志的作用之一!
在Python中,有一个logging模块,专门提供日志相关功能,下面来了解一下!
1.1、日志基本配置
import logging
# 如果在pycharm中打不开log格式文件,需要安装对应插件!具体方式为新建个后缀为log格式的文件,双击打开会提示安装!
logging.basicConfig( # 基本配置
filename='access.log', # 指定日志输出至指定文件,不指定文件则默认打印至终端
filemode='at', # 文件读写模式
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
# %(asctime)s,格式化时间。
# %(name)s,logger名字,没有指定默认就是root;loggers是日志产生者,产生的日志传递给handles由它控制输出
# %(levelname)s,文本形式的日志级别,有debug(调试);info(信息);warring(警告);error(错误);critical(紧急)。
# %(module)s,调用日志的函数模块名
# %(message)s,输出的消息
datefmt='%Y-%m-%d %H:%M:%S', # 替代上面的asctime
level=10 # debug(调试):10;info(信息):20;warring(警告):30;error(错误):40;critical(紧急):50
)
logging.error('程序错误信息')
1.2、日志字典配置
上述的展示仅为基本配置,在实际运用中,我们将日志的所有配置写入一个专用的字典,通过加载字典,让logging实现一系列日志相关操作,这样更为灵活方便!
1.2.1、日志字典概览
LOGGING_DICT = {
'version': 1, # 可选项,字典版本号。
'disable_existing_loggers': False, # 可选项,关闭已存在日志,默认即可!
'formatters': {}, # 必选项,定义各种日志格式
'filters': {}, # 可选项,日志过滤器!
'handlers': {}, # 必选项,日志接收者,用于控制日志输出至不同位置
'loggers': {} # 必选项,日志产生者,产生不同级别日志,并传递给handler,让它定义输出位置
}
1.2.2、日志格式定义
1.2.2.1、参数定义:
%(name)s Logger的名字, 没有指定默认就是root
%(levelno)s 数字形式的日志级别(10, 20, 30, 40, 50)
%(levelname)s 文本形式的日志级别(debug调试, info信息, warning警告, error错误, critical紧急)
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息
1.2.2.2、输出格式
# 定义日志的输出格式: 其中的%(name)s为调用logging模块后, 使用logging.getlogger()时指定的日志名
standard_format = '[%(asctime)s] [%(threadName)s:%(thread)d] [task_id:%(name)s] [%(filename)s:%(lineno)d]' '[%(levelname)s] [%(message)s]' # 标准输出,最详细!
simple_format = '[%(levelname)s] [%(asctime)s] [%(filename)s:%(lineno)d] %(message)s' # 简单输出
test_format = '%(asctime)s] %(message)s' # 测试输出,信息量最少!
1.2.3、输出格式formatters
'formatters': { # 必选项,定义各种日志格式
# formatters 加s代表可以设置很多个不同的日志格式
# standard, simple, test 主要目的是通过自定义这些名字让"handlers"中拿到下面自定义的日志格式的表现形式(提示: 自定义的日志格式命名可以修改)。
'standard': {
# standard_format 这里可以指定你自定义的日志格式表现的形式。这里是一个变量, 即上述定义的日志输出格式.
'format': standard_format
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
}
1.2.4、日志接收者handlers
# handlers是日志接收者, 用于控制日志的输出位置。不同的handler可将日志输出到不同的位置.
'handlers': {
# console, default, other 这是你自定义的handler名
# 输出位置: 打印到终端的日志, 由下面的class对应的logging.StreamHandler控制
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # class 指定日志输出的形式并打印到屏幕
'formatter': 'simple' # 指定日志格式输出的形式, 指向formatters的格式simple。
},
# 输出位置: 输出日志到文件, 由下面的class对应的logging.handlers.RotatingFileHandler控制
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
# RotatingFileHandler,日志轮转由下面的maxBytes与backupCount互相组合使用, 防止日志文件内容过大导致读取困难(即满足轮转的要求后, 将指定的文件"a1.log"重命名, 再新建一个"a1.log"文件, 存放最新的日志内容.)。 # 日志轮转的意义在于当日志文件存储数据过大的情况下。不应该让你的日志在日志文件里不断的累加。如果你的日志文件过大几的话,你打开文件的话就会非常的慢。所以我们需要定期的分割。
'formatter': 'standard',
'filename': 'a1.log',
'maxBytes': 1024 * 1024 * 5, # 最大轮转值, 超过后日志文件开始轮转,默认单位是字节Bytes,这里是: 5M!
'backupCount': 5, # 文件数量上限, 超过则删除最老的日志文件。
'encoding': 'utf-8', # 日志文件的编码格式
},
# 输出位置: 你指定的文件"a2.log", 由下面的class对应的logging.FileHandler控制
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # FileHandler,指定日志输出的形式输出到文件中。
'formatter': 'test',
'filename': 'a2.log', # 必须要指定文件路径,如果是在项目中,我们需要使用pathlib等规范化路径。
'encoding': 'utf-8',
},
},
1.2.5、日志产生者loggers
# loggers是日志的产生者, 负责产生不同级别的日志, 产生的日志会传递给上面的handlers中, 让handlers中的每个自定义的"handler"控制输出的位置。
'loggers': {
# "logger" 指定''这种空形式, 在执行logging.getLogger(key)时会判断,如果指定的key在下面的这些"logger"中都没找到, 就会把自定义key交给''中的"logger"处理, 处理以后, 交给上面的handlers进行处理
'': {
'handlers': ['pay', 'console_test'],
# handlers 这里指定你要交给的Handler路径, 交给"default"和"console"上面我们"handlers"中自定义的"handler"处理.
'level': 'DEBUG', # 这里也设置了日志级别,而handler中又设置了日志级别,双层限制。当使用logger_obj.info()这样的功能输入内容时. 会进行判断。如日志级别满足,则被收取到。满足以后会交给handlers中你自定义的"handler"来进行第二次筛选, 如果又满足,那么就会被你相应的"handler"功能, 进行处理。loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会将日志向上层层传递
},
'console_test': {
'handlers': ['console_test', ],
'level': 'DEBUG',
'propagate': False,
}
}
1.2.6、加载日志字典配置
# 先导入日志字典配置,再导入logging的config(logging作者没在init导入config)
logging.config.dictConfig(LOGGING_DICT)
log1 = logging.getLogger('console_test') #
log1.warning('测试logging日志字典配置')