小屌丝:鱼哥,最近你不讲究了啊~
小鱼:啥情况??
小屌丝:你竟然对我有隐瞒。。。
小鱼:我对你,除了妹子,其他的哪有隐藏的!!
小屌丝:Python日支输出方法,就对我有隐藏。
小鱼:没有啊,日志输出不都是用logging模块嘛。
小屌丝:不,你用的不是logging模块, 妹子都跟我说了。
小鱼:…
开心,可能是因为要get新的
姿势知识!
不管咋样,为了当初的约定, 小鱼忍着泪水,Share一次Python的日志输出内容。
关于Python的日志, 今天分享两个重量级模块:
话不多说,直接上车,开整!
日志等级 | 描述 |
---|---|
DEBUG | 最详细的日志信息,典型应用场景是 问题诊断 |
INFO | 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作 |
WARNING | 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的 |
ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息 |
敲黑板
日志级别等级排序:critical > error > warning > info > debug
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进行一次性配置 |
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
# 打印日志级别
def test_logging():
logging.debug('Python debug')
logging.info('Python info')
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
test_logging()
运行结果:
敲黑板
当指定一个日志级别之后,会记录大于或等于这个日志级别的日志信息,小于的将会被丢弃, 默认情况下日志打印只显示大于等于 WARNING 级别的日志。
通过 logging.basicConfig() 可以设置 root 的日志级别,和日志输出格式。
logging.basicConfig() 关键字参数:
关键字 | 描述 |
---|---|
filename | 创建一个 FileHandler,使用指定的文件名,而不是使用 StreamHandler。 |
filemode | 如果指明了文件名,指明打开文件的模式(如果没有指明 filemode,默认为 ‘a’)。 |
format | handler 使用指明的格式化字符串。 |
datefmt | handler 使用指明的格式化字符串。 |
level | 指明根 logger 的级别。 |
stream | 使用指明的流来初始化 StreamHandler。该参数与 ‘filename’ 不兼容,如果两个都有,‘stream’ 被忽略。 |
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
# 打印日志级别
def test():
logging.basicConfig(level=logging.DEBUG)
logging.debug('Python debug')
logging.info('Python info')
logging.warning('Python warning')
logging.error('Python Error')
logging.critical('Python critical')
logging.log(2,'test')
test()
运行结果
敲黑板:
Logging.basicConfig() 需要在开头就设置,在中间设置并无作用
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
# 打印日志级别
logging.basicConfig(filename='../test.log', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
在脚本的上级目录,会生成 test.log文件
日期对日志的重要性,不言而喻,所以,我们要学会使用日期格式,
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
# 显示消息时间
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning(' this event is logged.')
运行结果:
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
# 更改显示消息的格式
logging.basicConfig(format='%(levelname)s:%(message)s',level=logging.DEBUG)
logging.debug('Python message format Debug')
logging.info('Python message format Info')
logging.warning('Python message format Warning')
logging四大组件,好比四大护法。必不可少,我们来看看!
组件名称 | 对应类名 | 功能描述 |
---|---|---|
日志器 | Logger | 暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效 |
处理器 | Handler | 将 logger 创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
Logger是一个树形层级结构,在使用接口 debug,info,warn,error,critical 之前必须创建 Logger 实例:
创建方法
logger = logging.getLogger(logger_name)
创建Logger实例后,可以使用以下方法进行日志级别设置,增加处理器 Handler:
logger.setLevel(logging.ERROR)
: 设置日志级别为 ERROR,即只有日志级别大于等于 ERROR 的日志才会输出;
logger.addHandler(handler_name)
:为 Logger 实例增加一个处理器;
logger.removeHandler(handler_name)
: 为 Logger 实例删除一个处理器;
Handler 处理器类型有很多种,比较常用的有三个:
StreamHandler创建方法:
sh = logging.StreamHandler(stream=None)
创建 StreamHandler 之后,可以通过使用以下方法:
代码实例
# 指定日志级别,低于WARN级别的日志将被忽略
ch.setLevel(logging.WARN)
# 设置一个格式化器formatter
ch.setFormatter(formatter_name)
# 增加一个过滤器,可以增加多个
ch.addFilter(filter_name)
# 删除一个过滤器
ch.removeFilter(filter_name)
Handlers 和 Loggers 可以使用 Filters 来完成比级别更复杂的过滤。
Filter 基类只允许特定 Logger 层次以下的事件。
创建方法
filter = logging.Filter(name='')
使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。
创建方法
formatter = logging.Formatter(fmt=None, datefmt=None)
敲黑板
fmt 是消息的格式化字符串,datefmt 是日期字符串。
- 如果不指明 fmt,将使用 ‘%(message)s’ ;
- 如果不指明 datefmt,将使用 ISO8601 日期格式。
[loggers]
keys=root,infoLogger
[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler
[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0
[handlers]
keys=consoleHandler,fileHandler
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)
#args = (sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('../log/testlog.log', 'a')
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
import logging
import os.path
import time
class Logger(object):
def __init__(self, logger):
"""
指定保存日志的文件路径,日志级别,以及调用文件
将日志存入到指定的文件中
:param logger:
"""
# 拼接日志文件夹,如果不存在则自动创建
cur_path = os.path.dirname(os.path.realpath(__file__))
log_path = os.path.join(os.path.dirname(cur_path), 'logs')
if not os.path.exists(log_path):os.mkdir(log_path)
# 创建一个logger
self.logger = logging.getLogger(logger)
self.logger.setLevel(logging.DEBUG)
# 创建一个handler,用于写入日志文件
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
# log_path = os.path.dirname(os.getcwd()) + '/Logs/'
# log_name = log_path + rq + '.log'
log_name = os.path.join(log_path,'%s.log ' %rq)
fh = logging.FileHandler(log_name)
fh.setLevel(logging.INFO)
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
def getlog(self):
return self.logger
如果不理解日志的封装,可以参照小鱼的这篇博文
或者参照Selenium/Appium的系列博文:
解释完logging模块,就到了今天的主角 loguru模块。
登场方式,必须闪亮。
凡事都要第一步,loguru 也让不开的安装
pip install loguru
当然,
如果不想每次都pip安装,直接跳转这两篇:
安装完成后, 我们来小试一下,看看有啥能耐:
编写代码:
# -*- coding:utf-8 -*-
# @Time : 2021-10-21
# @Author : carl_DJ
from loguru import logger
#定义日志输出debug级别
logger.debug("output debug message")
运行结果:
2021-10-21 23:25:12.565 | DEBUG | __main__::5 - output debug message
解析
输出的内容包含:
2021-10-21
23:25:12.565
DEBUG
__main__::5
output debug message
在loguru中,输出日志文件只需要一个 add() 函数即可:
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
#输出日志文件
logger.add('testDJ.log')
#设置日志输出等级
logger.debug("output debug message")
我们添加一些参数,指定日志输出格式和级别:
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
#定义日志输出格式、级别
log = logger.add('testInfo.log', format="{time} | {level} | {message}", level="INFO")
#设置日志输出等级
logger.debug("output debug message")
logger.info('output info message')
敲黑板
因为设置的level 是 "INFO"级别,
所以,debug级别不会输出的。
小屌丝:鱼哥,我不想把日志写入文件,怎么操作呢?
小鱼:嗯,你想的很全面,直接用logger.remove()就可以了。
当我们不想把日志写入文件,使用logger.remove()方法。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
#定义日志输出格式、级别
log = logger.add('testInfo.log', format="{time} | {level} | {message}", level="INFO")
#输出info级别日志
logger.info("output debug message")
#移除日志文件内容
logger.remove(log)
#输出info级别日志
logger.info('output another info message')
输出结果:
敲黑板:
文件中只记录了 第一条信息,
第二条被使用logger.remove()方法,所以不会写入日志里面了。
小屌丝:这个可以啊, 那能不能指定日志的生成方式,例如:按照文件大小,时间等来指定生成。
小鱼:这也可以,直接在logger.add()方法里面配置rotation 值即可。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
# 超过300M就新生成一个文件
logger.add("size.log", rotation="300 MB")
# 每天凌晨1点生成一个新文件
logger.add("time.log", rotation="1:00")
# 2周生成一个新文件
logger.add("size.log", rotation="2 week")
小屌丝:指定日志文件有效期,这个能实现吗?
小鱼:弄破了博勒姆( no problem)…
直接在logger.add()方法里面配置retention 值即可。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
# 保留日志时长为30天
logger.add("file.log", retention="30 days")
小屌丝:如果日志文件太大,是否可以压缩呢
小鱼:那肯定的,logger.add()方法里面配置compression 值即可。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
# 日志文件压缩成zip格式
logger.add("file.log", compression="zip")
小屌丝:这给loguru厉害的,那我再升级难度问一下
小鱼:请出题。
小屌丝:请问,loguru能否捕获异常呢??
小鱼:额,容我想一下。
小屌丝:怎么样,被难住了吧
小鱼:就这?? 我是在想,loguru一共有几种捕获异常的方法。
小屌丝:…
catch,是以装饰器的形式存在,
用法为:@logger.catch
我们就算一道除法题,来看看catch捕获异常后,输出的信息是否足够详细。
题目:用3除以 0。
这道题,3岁小孩都知道 除数不能为0。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
#使用catch装饰器来捕获异常
@logger.catch
def test_function(a):
return 3/a
#给a传参为0
test_functioin(0)
我们可以看到,输出的信息,非常的详细,很奈斯!
第二种捕获异常方法,使用exception。
代码实例
# -*- coding:utf-8 -*-
# @Time : 2021-10-22
# @Author : carl_DJ
from loguru import logger
def x_function(a):
try:
return 3/a
except ZeroDivisionError:
#exception捕获异常
logger.exception("exception!")
#给a传参为0
x_function(0)
运行结果
妥妥的,异常信息也很奈斯!!
小屌丝:妥,我要转粉为loguru了,谁都拦不住!!
今天分享了两个日志模块:logging和loguru,
每个模块都有自己的优点,选择哪一个,只看各位同学的使用习惯了。
但是,不管怎样,使用自己熟练的方法,让程序零BUG,就是最初的本质的。
不要为了高大上而高大上。
今天的内容很多,如果一次记不住,键位收藏慢慢吸收!!