logging模块,Python3自带用来记录日志的模块。
因为工作需要用到关于日志的,最近一直都在看关于日志模块的东西,百度了很多文章,可惜都是看的让人一头雾水,最后运气不错,找到一篇很详细的文章。传送门:https://www.cnblogs.com/testdjt/p/7834856.html
logging模块默认定义了以下几个日志等级
日志等级 (level) | 描述 |
DEBUG | 最详细的日志信息,典型应用场景是 问题诊断 |
INFO | 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作 |
WARNING | 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的 |
ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息 |
级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUG >NOTSET 日志的信息量会依次减少
当指定一个日志级别之后,会记录大于或等于这个日志级别的日志信息,小于的将会被丢弃
Logging模块提供了两种记录日志的方式:
第一种方式是使用logging提供的模块级别的函数
第二种方式是使用Logging日志系统的四大组件
logging模块定义的模块级别函数
函数 | 说明 |
logging.debug(msg, *args, **kwargs) | 创建一条严重级别为DEBUG的日志记录 |
logging.info(msg, *args, **kwargs) | 创建一条严重级别为INFO的日志记录 |
logging.warning(msg, *args, **kwargs) | 创建一条严重级别为WARNING的日志记录 |
logging.error(msg, *args, **kwargs) | 创建一条严重级别为ERROR的日志记录 |
logging.critical(msg, *args, **kwargs) | 创建一条严重级别为CRITICAL的日志记录 |
logging.log(level, *args, **kwargs) | 创建一条严重级别为level的日志记录 |
logging.basicConfig(**kwargs) | 对root logger进行一次性配置 |
import logging
logging.debug("a")
logging.info("b")
logging.warning('c')
logging.error('d')
logging.critical('e')
只有后面三个打印出来了,因为默认级别是logging.warning,低于该级别的就不输出了
logging.basicConfig(level=logging.DEBUG)# 设置日志级别,需要大写
logging.debug("a")
logging.info("b")
logging.warning('c')
logging.error('d')
logging.critical('e')
Logging.basicConfig()需要在开头就设置,在中间设置没有作用
通过logging.basicConfig可以设置root的日志级别,和日志输出格式。
logging模块的四大组件
组件名称 | 对应类名 | 功能描述 |
日志器 | Logger | 提供了应用程序可一直使用的接口 |
处理器 | handler | 将logger创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
logging模块提供的模块级别的那些函数实际上也是通过这几个组件的相关实现类来记录日志的,只是在创建这些类的实例时设置了一些默认值
这些组件之间的关系描述:
日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;不同的处理器(handler)可以将日志输出到不同的位置;
日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
logger类
a = logging.getLogger('mylogger')
getLogger()返回一个logger实例,可以传入一个logger的名字,如果没有传,默认是root logger
如果传了,比如这里是mylogger,在输出日志时候,就会显示是mylogger下的
上面logging.info等,也是显示在root下的
a.setLevel(logging.INFO)
通过setLeverl()方法可以设置logger的日志输出级别
a.debug('xxxxxx')
a.info('cccccc')
#Logger.addHandler() 和 Logger.removeHandler() 为该logger对象添加 和 移除一个handler对象
#Logger.addFilter() 和 Logger.removeFilter() 为该logger对象添加 和 移除一个filter对象
需要之前的logging.basicConfig()的设置,才会输出这个,因为logger的handler还没有加
handler类
通过handler对象可以把日志内容写到不同地方,python提供了十几种实用的handler,比较常用的有:
类型 | 说明 |
StreamHandler | 输出到控制台 |
FileHandler | 输出到文件 |
BaseRotatingHandler | 可以按时间写入到不同的日志中。比如将日志按天写入不同的日期结尾的文件文件 |
SocketHandler | 用TCP网络连接写LOG |
DatagramHandler | 用UDP网络连接写LOG |
SMTPHandler | 把LOG写成EMAIL邮寄出去 |
fh = logging.FileHandler('D:/lesson/test1.txt')#创建一个输出到文件的handler对象,这个对象叫fh
fh.setLevel(logging.INFO)#给fh加上日志输出级别
a.addHandler(fh)#给logger添加handler
a.info('sfsdf')
常用的方法
Handler.setLevel() 设置handler将会处理的日志消息的最低严重级别
Handler.setFormatter() 为handler设置一个格式器对象
Handler.addFilter() 和 Handler.removeFilter() 为handler添加 和 删除一个过滤器对象
Formater类
字段/属性名称 | 使用格式 | 描述 |
---|---|---|
asctime | %(asctime)s | 日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896 |
created | %(created)f | 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值 |
relativeCreated | %(relativeCreated)d | 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的) |
msecs | %(msecs)d | 日志事件发生事件的毫秒部分 |
levelname | %(levelname)s | 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') |
levelno | %(levelno)s | 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) |
name | %(name)s | 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger |
message | %(message)s | 日志记录的文本内容,通过 msg % args 计算得到的 |
pathname | %(pathname)s | 调用日志记录函数的源码文件的全路径 |
filename | %(filename)s | pathname的文件名部分,包含文件后缀 |
module | %(module)s | filename的名称部分,不包含后缀 |
lineno | %(lineno)d | 调用日志记录函数的源代码所在的行号 |
funcName | %(funcName)s | 调用日志记录函数的函数名 |
process | %(process)d | 进程ID |
processName | %(processName)s | 进程名称,Python 3.1新增 |
thread | %(thread)d | 线程ID |
threadName | %(thread)s | 线程名称 |
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')#配置日志输出格式
fh.setFormatter(formatter)#给handle加上格式
a.info('1244555')
formatter不像handler还需要先创建一个对应的实例,直接配置就可以了
详细学习了logging模块之后,开始封装自己的log类
1.需要把日志输出到指定路径,文件名为日期.log
2.需要记录所有文件信息,并且报错信息单独存放,这需要不同的handler
3.info和error需要不同的日志格式
我的文件结构
通过os.path.dirname(os.getcwd())获得项目名,也就是Vuiki。项目路径不同,使用的方法也不同。
使用os.path.join()将路径进行拼接,形成日志存放的路径
import logging
import time
import os
class mylog(object):
def __init__(self,logger_name):
#创建一个logger
self.logger= logging.getLogger(logger_name)
self.logger.setLevel(logging.INFO)
#设置日志存放路径,日志文件名
#获取本地时间,转换为设置的格式
rq = time.strftime('%Y%m%d%H%M',time.localtime(time.time()))
#设置所有日志和错误日志的存放路径
all_log_path = os.path.join(os.path.dirname(os.getcwd()),'Logs/All_Logs/')
error_log_path = os.path.join(os.path.dirname(os.getcwd()),'Logs/Error_Logs/')
#设置日志文件名
all_log_name = all_log_path + rq +'.log'
error_log_name = error_log_path + rq +'.log'
#创建handler
#创建一个handler写入所有日志
fh = logging.FileHandler(all_log_name)
fh.setLevel(logging.INFO)
#创建一个handler写入错误日志
eh = logging.FileHandler(error_log_name)
eh.setLevel(logging.ERROR)
#创建一个handler输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
#定义日志输出格式
#以时间-日志器名称-日志级别-日志内容的形式展示
all_log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#以时间-日志器名称-日志级别-文件名-函数行号-错误内容
error_log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(lineno)s - %(message)s')
#将定义好的输出形式添加到handler
fh.setFormatter(all_log_formatter)
ch.setFormatter(all_log_formatter)
eh.setFormatter(error_log_formatter)
#给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(eh)
self.logger.addHandler(ch)
def getlog(self):
return self.logger
下面是测试代码
from Common import logger
logger = logger.mylog('test').getlog()
try:
logger.info('开始测试...')
r = 10/0
logger.info('result:',r)
except ZeroDivisionError as e:
logger.error('tests',exc_info=1)
logger.info('end')
这是输出到控制台的日志信息
这是所有的日志信息
这是错误的日志信息
使用exc_info=1可以把报错信息完整的打印进日志
另外在实际使用时,可以在日志功能实现文件最后直接创建实例。在需要使用时,直接import该实例使用。这样能保证日志输出只有一个地方,避免出现日志重复打印的问题。