python之日志模板
1、什么是日志
日志是一种可以追踪某些软件运行时所发生事件的方法,可以在程序的关键部分调用日志记录程序运行的状态与结果。
在Python中打印功能只能在控制台显示,某个节点的数据无法保存在文件中。当一个程序上线之后,我们需要记录用户的一些相关操作,和程序的运行状态,重要信息的打印,后期的查询与数据分析,日志记录就非常重要了。
日志的主要功能:
- 程序调试
- 用户操作行为记录
- 程序运行故障分析与问题定位
2、日志级别
由于日志消息的重要性的不同,方面信息的查询与快速定位,日志信息的保存分成不同的等级,根据等级的不同,我们可以把日志保存到不同的文件中
日志级别:DEBUG < INFO < WARNING < ERROR < CRITICAL
日志等级(level) | 描述 |
---|---|
DEBUG | 最详细的日志信息,典型应用场景是 问题诊断,用于记录不重要的信息 |
INFO | 通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作,操作行为记录,重要信息打印 |
WARNING | 当某些不期望的事情发生时记录的信息,但是此时应用程序还是正常运行的 |
ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息 |
开发应用程序或部署开发环境时,可以使用DEBUG或INFO级别的日志获取尽可能详细的日志信息来进行开发或部署调试;应用上线或部署生产环境时,应该使用WARNING或ERROR或CRITICAL级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。
3、日志模块logging的使用
3.1 logging模块的级别:
#默认级别是WARNING ,低于这个级别的信息不打印,可以设置,level=10 #或 level=logging.DEBUG (日志的级别需要大写) CRITICAL = 50 #FATAL = CRITICAL ERROR = 40 WARNING = 30 #WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 #不设置
3.2日志模块的高级配置
logger |
产生日志的对象 |
Filter | 过滤日志的对象 |
Handler | 接收日志然后控制打印到不同的地方,FileHandler用来打印到文件中,StreamHandler用来打印到终端 |
Formatter | 可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式 |
3.3 日志配置文件的使用,直接使用模板就行
使用时候:你可以自己配置相关参数,在其他模块中,直接导入load_logger使用
# -*- coding: utf-8 -*- # @Time : 2021/7/28 15:34 # @Author : March # @File : config.py #配置日志文件 """ logging配置 """ import os import time import logging.config # 1、定义三种日志输出格式,日志中可能用到的格式化串如下 # %(name)s Logger的名字 getLogger('name') 默认__name__ 或者空值。区分日志种类 # %(levelno)s 数字形式的日志级别 # %(levelname)s 文本形式的日志级别,推荐使用 # %(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用户输出的消息 # 2、强调:其中的%(name)s为getlogger时指定的名字,一行太长了,可以加\换行 #这个格式是我常用的格式,需要的话可以自己设定 standard_format = '[%(asctime)s:%(levelname)s:task_id:%(name)s in %(filename)s:%(pathname)s:%(lineno)d]' \ '>>>:%(message)s' #[2021-09-14 23:31:45,667:INFO:task_id:root in 日志模块测试.py:E:/pythonweb/python_study/日志模块测试.py:131]>>>:日志文件启动 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' #[INFO][2021-09-14 23:31:45,667][日志模块测试.py:131]日志文件启动 error_format = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' #2021-09-14 23:31:45,669 ERROR: 我这里保存了错误信息 [in E:/pythonweb/python_study/日志模块测试.py:136] #设置日志文件 logfile_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #项目的更目录 #这里设置了两个日志文件,一个用于保存正常的数据,一个记录程序错误数据记录 logfile_name =time.strftime('%Y-%m-%d')+'_info.log' logfile_error =time.strftime('%Y-%m-%d')+'_error.log' # log文件的路径 logfile_path = os.path.join(logfile_dir, 'log',logfile_name) error_logfile_path = os.path.join(logfile_dir, 'log',logfile_error) # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(os.path.join(logfile_dir, 'log')): os.mkdir(os.path.join(logfile_dir, 'log')) # 3、日志配置字典 LOGGING_DIC = { #默认设置 'version': 1, 'disable_existing_loggers': False, #日志信息的格式 #standard,simple,error可改,需要与handlers中的formatter对应,formatters,format关键字无法修改 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, 'error': { 'format': error_format }, }, #暂时不需要 'filters': {}, #日志的接受者,根据不同的配置,保存文件到不同的位置 'handlers': { #console,default,error关键字可以自己修改,需要与logger中的handlers对应 #打印到终端的日志 'console': { 'level': 'DEBUG', #日志的级别 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple'#需要的日志格式 }, #打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转 'formatter': 'standard', # 可以定制日志文件路径 'filename': logfile_path, # 日志文件 'maxBytes': 1024*1024*100, # 日志大小 文件最大为100M,超过之后会分割文件 'backupCount': 15, #文件最多保留15个 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, #只保存出现错误及以上的日志,数据很少 'error': { 'level': 'ERROR', 'class': 'logging.FileHandler', # 保存到文件,没有分割文件的功能 'formatter': 'error', 'filename': error_logfile_path, 'encoding': 'utf-8', }, }, #日志的生产者 'loggers': { #logging.getLogger(__name__)拿到的logger配置, #空的指定对象,在不指定的情况下,默认使用该对象中的handlers '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制) 'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递 }, 'error': { 'handlers': ['error',], 'level': 'DEBUG', #可以使用数字10 'propagate': False, }, }, } #调用日志对象 def load_logger(logger_name=''): #传入一个日志的类型,名称为loggers中的,如果没有默认'' logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置 #logger_name可以设置为执行文件名,当不存在的时候默认都是使用''loggers对象 logger = logging.getLogger(logger_name) # 生成一个log实例变量对应日志配置中的%(name)s return logger #返回一个日志对象,可以直接调用 # load_my_logging_cfg() if __name__ == '__main__': logger=load_logger() #该函数返回一个对象 logger.info('日志文件启动') logger.info('由于我没有传logger的名字,或者传了一个不存在的,所以我使用默认空的配置') #由于error名称的logger存在,所以会直接调用error对应的相关配置 error_logger = load_logger('error') error_logger.error('我这里保存了错误信息') #只能保存error上的级别
修改成类使用的方式:
""" #配置日志文件 """ # 日志文件不存在,创建 if not os.path.isdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')): os.mkdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')) class Logger(): standard_format = '[%(asctime)s:%(levelname)s:task_id:%(name)s in %(filename)s:%(pathname)s:%(lineno)d]' \ '>>>:%(message)s' simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' error_format = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' # log文件的全路径 logfile_dir = os.path.dirname(os.path.abspath(__file__)) # logs文件的目录 logfile_path = os.path.join(logfile_dir, 'logs', 'info.log') error_logfile_path = os.path.join(logfile_dir, 'logs', 'error.log') logging_dic = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, 'error': { 'format': error_format }, }, 'filters': {}, # 日志的执行者 'handlers': { # 打印到终端的日志 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, # 打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转 'formatter': 'standard', 'filename': logfile_path, # 日志文件 'maxBytes': 1024 * 1024 * 20, # 日志大小 5M 'backupCount': 15, 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, # 只保存出现错误及以上的日志 'error': { 'level': 'ERROR', 'class': 'logging.FileHandler', # 保存到文件,没有分割文件的功能 'formatter': 'error', 'filename': error_logfile_path, 'encoding': 'utf-8', }, }, # 日志的生产者 'loggers': { # logging.getLogger(__name__)拿到的logger配置, # 空的指定对象,在不指定的情况下,默认使用该对象中的handlers '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 'level': 'INFO', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制) 'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递 }, 'error': { 'handlers': ['error', ], 'level': 'DEBUG', 'propagate': False, }, }, } #调用日志对象 @classmethod def load_logger(cls,logger_name=None): #传入一个日志的类型,名称为loggers中的,如果没有默认'' logging.config.dictConfig(cls.logging_dic) # 导入上面定义的logging配置 logger = logging.getLogger(logger_name) # 生成一个log实例变量对应日志配置中的%(name)s return logger #返回一个日志对象,可以直接调用