Python日志处理(logging模块)
日志追踪程序运行时候发生的事件,开发当中在程序中添加logging模块,记录程序日志log。通过log的分析,可以方便用户了解系统或者软件的运行状况;
如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;
日志的记录
一条日志信息对应的是一个事件的发生,而一个事件通常需要包括以下几个内容:
- 事件发生的时间
- 事件发生的位置
- 事件的严重程度–日志级别
- 事件的内容
说明:日志格式的规范性能帮助开发人员快速定位,当然针对不同的需求,可以包含其他信息,输入一条日志时候,日志内容和日志级别是需要开发人员明确支出的,对于其他字段信息,按需。
日志级别
不同的应用程序所定义的日志等级可能会有所差别,分的详细点的会包含以下几个等级:
- DEBUG
- INFO
- NOTICE
- WARNING
- ERROR
- CRITICAL
- ALERT
- EMERGENCY
重点:日志功能的实现(logging模块介绍)
几乎所有的开发语言都会内置日志相关功能,或者会有比较优秀的第三方库来提供日志操作功能,比如:log4j, log4php等。
python自身也提供了一个用于记录日志的标注模块logging
logging模块的使用方法介绍
作为开发者,我们可以通过以下三种方式来配置logging:
- 使用python代码显式的创建loggers, handlers,和formatters并分别调用他们的配置函数;
- 创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
- 创建一个包含配置信息的dict, 然后把它传递给dictConfig()函数
需要说明的是, logging.basicConfig()也属于第一种方式,它只是对loggers, handleers和formatters的配置函数进行了封装。另外,第二种配置方式相对于第一种配置方法的优点在于,他将配置信息和代码进行了分离,之一方面降低了日志的维护成本,同时还使得非开发人员能够很容易的去修改日志配置。
方式一
def logs_method1(filePath):
"""
写日志方式1, 1)使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;
:return:
"""
# 创建一个日志器logger并设置其日志级别为DEBUG
logger = logging.getLogger('simple_logger')
logger.setLevel(logging.DEBUG)
# 创建一个流处理器handler并设置其日志级别为DEBUG
handler = logging.StreamHandler(open(filePath,'a'))
handler.setLevel(logging.DEBUG)
# 创建一个格式器formatter并将其添加到处理器handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
# 为日志器logger添加上面创建的处理器handler
logger.addHandler(handler)
# # 日志输出
# logger.debug('debug message')
# logger.info('info message')
# logger.warn('warn message')
# logger.error('error message')
# logger.critical('critical message')
return logger
方式二
def logs_method2():
"""
写日志方法2 : 2)创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
:return:
"""
# 读取日志配置文件内容
logging.config.fileConfig('logs/log_config/logging.conf')
# 创建一个日志器logger
logger = logging.getLogger('simpleExample')
# 日志输出
# logger.debug('debug message 22')
# logger.info('info message 22')
# logger.warn('warn message 22')
# logger.error('error message 22')
# logger.critical('critical message 22')
return logger
配置文件:
#logger.conf
###############################################
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHander,errorFileHander,logFileHander
[formatters]
keys=form01,form02,form03
#################################################
[logger_root]
level=DEBUG
handlers=consoleHander
[logger_simpleExample]
level=DEBUG
handlers=consoleHander,errorFileHander,logFileHander
qualname=simpleExample
propagate=0
###############################################
[handler_consoleHander]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=form03
[handler_errorFileHander]
class=FileHandler
level=ERROR
formatter=form03
encoding='utf8'
args=('logs/log_record/error.log', 'a')
[handler_logFileHander]
class=logging.handlers.RotatingFileHandler
level=INFO
formatter=form03
encoding='utf8'
args=('logs/log_record/log.log', 'a', 500*1024*1024, 5)
###############################################
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form03]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
配置文件格式说明
- 配置文件中一定要包含loggers, handlers,formatters 这些section;他们通过keys这个option来指定该配置文件中已经定义好的loggers, handlers和formatters, 多个值之间用逗号分隔; 另外 loggers这个section中的keys 一定要包含root这个值;
- loggers、handlers、formatters中所指定的日志器、处理器和格式器都需要在下面以单独的section进行定义。seciton的命名规则为[logger_loggerName]、[formatter_formatterName]、[handler_handlerName]
- 定义logger的section必须指定level和handlers这两个option,level的可取值为DEBUG、INFO、WARNING、ERROR、CRITICAL、NOTSET,
- 对于非root logger来说,除了level和handlers这两个option之外,还需要一些额外的option,其中qualname是必须提供的option,它表示在logger层级中的名字,在应用代码中通过这个名字得到logger;查找不到名字时候会使用上级的配置;propagate是可选项,其默认是为1,表示消息将会传递给高层次logger的handler,通常我们需要指定其值为0,这个可以看下下面的例子;另外,对于非root logger的level如果设置为NOTSET,系统将会查找高层次的logger来决定此logger的有效level。
- 定义handler的section中必须指定class和args这两个option,level和formatter为可选option;class表示用于创建handler的类名,args表示传递给class所指定的handler类初始化方法参数
,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;
定义formatter的sectioin中的option都是可选的,其中包括format用于指定格式字符串,默认为消息字符串本身;datefmt用于指定asctime的时间格式,默认为’%Y-%m-%d %H:%M:%S’;class用于指定格式器类名,默认为logging.Formatter;
记录
记录自定义的脚本;完成日志的配置;可以满足大部分的开发需求:
在自己项目中导入该包就行;
init_logger.py
# coding:utf-8
"""
@author: Finks
@time: 2021/2/1 17:11
"""
import os
import logging
from logging import Logger
from logging.handlers import TimedRotatingFileHandler
'''
日志模块
1. 同时将日志打印到屏幕跟文件中
2. 默认值保留近30天日志文件
'''
def init_logger(log_dir, log_name=None):
# default value
if log_name == None: log_name = 'Log.log'
assert isinstance(log_name, str), f'log_name require string..but got {type(log_name)}'
logger = logging.getLogger(log_name)
logger.setLevel(logging.DEBUG)
# file handler for info
handler = TimedRotatingFileHandler(filename=os.path.join(log_dir, log_name), when='D', backupCount=30)
date_fmt = '%Y-%m-%d %H:%M:%S'
format_str = '[%(asctime)s] - %(filename)s [line:%(lineno)s] [%(levelname)s]: %(message)s'
formatter = logging.Formatter(format_str, date_fmt)
handler.setFormatter(formatter)
handler.setLevel(logging.INFO)
logger.addHandler(handler)
# console handler
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(console)
# file handler for error
handler = TimedRotatingFileHandler(filename=os.path.join(log_dir, "ERROR_{}".format(log_name)), when='D',
backupCount=30)
date_fmt = '%Y-%m-%d %H:%M:%S'
format_str = '[%(asctime)s] - %(filename)s [line:%(lineno)s] [%(levelname)s]: %(message)s'
formatter = logging.Formatter(format_str, date_fmt)
handler.setFormatter(formatter)
handler.setLevel(logging.ERROR)
logger.addHandler(handler)
return logger
if __name__ == "__main__":
logger = init_logger(log_dir=r'C:\Users\ymchen.Finks\Desktop')
logger.info("test")
logger.error("test2")
快捷方式
直接拷入这部分代码;可以满足大部分的需求;
import logging
from logging import handlers
# 日志路径
log_file = 'log.log'
logger = logging.getLogger(log_file)
# 设置日志等级
logger.setLevel(logging.INFO)
# 设置写入格式
fmt = '%(asctime)s - %(filename)s[line:%(lineno)d] - [%(levelname)s]: %(message)s'
# 定义往文件中写入句柄和往屏幕上写入的句柄, 并设置格式(when: 设置间隔时间单位,backCount:保留日志的个数,超过数量会删除)
t_handler = logging.handlers.TimedRotatingFileHandler(filename=log_file, when='D', backupCount=30)
s_handler = logging.StreamHandler()
t_handler.setFormatter(logging.Formatter(fmt))
s_handler.setFormatter(logging.Formatter(fmt))
# 添加句柄,完成配置!
logger.addHandler(s_handler)
logger.addHandler(t_handler)
logger.error('错误')
logger.info('你好!')
带颜色的日志
# coding:utf-8
"""
@author: Finks
@time: 2021/6/3 21:07
"""
import logging
import colorlog
log_colors_config = {
'DEBUG': 'white', # cyan white
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'blue',
}
logger = logging.getLogger('Log')
# 输出到控制台
console_handler = logging.StreamHandler()
# 输出到文件
file_handler = logging.FileHandler(filename='Log', mode='a', encoding='utf8')
logger.setLevel(logging.DEBUG)
console_handler.setLevel(logging.DEBUG)
file_handler.setLevel(logging.INFO)
# 日志输出格式
file_formatter = logging.Formatter(
fmt='[%(asctime)s.%(msecs)03d %(levelname)08s] : %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_formatter = colorlog.ColoredFormatter(
fmt='%(log_color)s[%(asctime)s.%(msecs)03d %(levelname)08s] : %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
log_colors=log_colors_config
)
console_handler.setFormatter(console_formatter)
file_handler.setFormatter(file_formatter)
# 重复日志问题:
# 1、防止多次addHandler;
# 2、loggername 保证每次添加的时候不一样;
# 3、显示完log之后调用removeHandler
if not logger.handlers:
logger.addHandler(console_handler)
logger.addHandler(file_handler)
console_handler.close()
file_handler.close()
if __name__ == '__main__':
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')
🐱