关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶、JS/安卓逆向等技术干货!

【01x00】日志的重要性

日志的作用非常重要,日志可以记录用户的操作、程序的异常,还可以为数据分析提供依据,日志的存在意义就是为了能够在程序在运行过程中记录错误,方便维护和调试,能够快速定位出错的地方,减少维护成本。每个程序员都应该知道,不是为了记录日志而记录日志,日志也不是随意记的。要实现能够只通过日志文件还原整个程序执行的过程,达到能透明地看到程序里执行情况,每个线程、每个过程到底执行到哪的目的。日志就像飞机的黑匣子一样,应当能够复原异常的整个现场乃至细节!

【02x00】常见日志记录方式

一、print()

最常见的是把输出函数 print() 当作日志记录的方式,直接打印各种提示信息,常见于个人练习项目里,通常是懒得单独配置日志,而且项目太小不需要日志信息,不需要上线,不需要持续运行,完整的项目不推荐直接打印日志信息,现实中也几乎没有人这么做。

二、自写模板

我们可以在不少小项目里面看到作者自己写了一个日志模板,通常利用 print() 或者 sys.stdout 稍微封装一下即可实现简单的日志输出,这里的 sys.stdout 是 Python 中的标准输出流,print() 函数是对 sys.stdout 的高级封装,当我们在 Python 中打印对象调用 print(obj) 时候,事实上是调用了 sys.stdout.write(obj+'\n')print() 将内容打印到了控制台,然后追加了一个换行符 \n

自写日志模板适合比较小的项目,可以按照自己的喜好编写模板,不需要太多复杂配置,方便快捷,但是这种记录日志的方式并不是很规范,有可能你自己觉得阅读体验不错,但是别人在接触你的项目的时候往往需要花费一定的时间去学习日志的逻辑、格式、输出方式等,比较大的项目同样不推荐这种方法。

一个简单的自写日志模板举例:

日志模板 log.py:

import sys
import traceback
import datetimedef getnowtime():return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")def _log(content, level, *args):sys.stdout.write("%s - %s - %s\n" % (getnowtime(), level, content))for arg in args:sys.stdout.write("%s\n" % arg)def debug(content, *args):_log(content, 'DEBUG', *args)def info(content, *args):_log(content, 'INFO', *args)def warn(content, *args):_log(content, 'WARN', *args)def error(content, *args):_log(content, 'ERROR', *args)def exception(content):sys.stdout.write("%s - %s\n" % (getnowtime(), content))traceback.print_exc(file=sys.stdout)

调用日志模块:

import loglog.info("This is log info!")
log.warn("This is log warn!")
log.error("This is log error!")
log.debug("This is log debug!")people_info = {"name": "Bob", "age": 20}try:gender = people_info["gender"]
except Exception as error:log.exception(error)

日志输出:

2021-10-19 09:50:58 - INFO - This is log info!
2021-10-19 09:50:58 - WARN - This is log warn!
2021-10-19 09:50:58 - ERROR - This is log error!
2021-10-19 09:50:58 - DEBUG - This is log debug!
2021-10-19 09:50:58 - 'gender'
Traceback (most recent call last):File "D:/python3Project/test.py", line 18, in <module>gender = people_info["gender"]
KeyError: 'gender'

三、Logging

在一个完整的项目中,大多数人都会引入专门的日志记录库,而 Python 自带的标准库 logging 就是专门为日志记录而生的,logging 模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。由标准库模块提供日志记录 API 的关键好处是所有 Python 模块都可以使用这个日志记录功能。所以,你的应用日志可以将你自己的日志信息与来自第三方模块的信息整合起来。

logging 模块虽然强大,但是其配置也是比较繁琐的,在大型项目中通常需要单独初始化日志、配置日志格式等等,K哥在日常使用中通常都会对 logging 做如下的封装写法,使日志可以按天保存,保留15天的日志,可以配置是否输出到控制台和文件,如下所示:

# 实现按天分割保留日志import os
import sys
import logging
from logging import handlersPARENT_DIR = os.path.split(os.path.realpath(__file__))[0]  # 父目录
LOGGING_DIR = os.path.join(PARENT_DIR, "log")              # 日志目录
LOGGING_NAME = "test"                                      # 日志文件名LOGGING_TO_FILE = True                                     # 日志输出文件
LOGGING_TO_CONSOLE = True                                  # 日志输出到控制台LOGGING_WHEN = 'D'                                         # 日志文件切分维度
LOGGING_INTERVAL = 1                                       # 间隔少个 when 后,自动重建文件
LOGGING_BACKUP_COUNT = 15                                  # 日志保留个数,0 保留所有日志
LOGGING_LEVEL = logging.DEBUG                              # 日志等级
LOGGING_suffix = "%Y.%m.%d.log"                            # 旧日志文件名# 日志输出格式
LOGGING_FORMATTER = "%(levelname)s - %(asctime)s - process:%(process)d - %(filename)s - %(name)s - line:%(lineno)d - %(module)s - %(message)s"def logging_init():if not os.path.exists(LOGGING_DIR):os.makedirs(LOGGING_DIR)logger = logging.getLogger()logger.setLevel(LOGGING_LEVEL)formatter = logging.Formatter(LOGGING_FORMATTER)if LOGGING_TO_FILE:file_handler = handlers.TimedRotatingFileHandler(filename=os.path.join(LOGGING_DIR, LOGGING_NAME), when=LOGGING_WHEN, interval=LOGGING_INTERVAL, backupCount=LOGGING_BACKUP_COUNT)file_handler.suffix = LOGGING_suffixfile_handler.setFormatter(formatter)logger.addHandler(file_handler)if LOGGING_TO_CONSOLE:stream_handler = logging.StreamHandler(sys.stderr)stream_handler.setFormatter(formatter)logger.addHandler(stream_handler)def logging_test():logging.info("This is log info!")logging.warning("This is log warn!")logging.error("This is log error!")logging.debug("This is log debug!")people_info = {"name": "Bob", "age": 20}try:gender = people_info["gender"]except Exception as error:logging.exception(error)if __name__ == "__main__":logging_init()logging_test()

输出日志:

INFO - 2021-10-19 11:28:10,103 - process:15144 - test.py - root - line:52 - test - This is log info!
WARNING - 2021-10-19 11:28:10,105 - process:15144 - test.py - root - line:53 - test - This is log warn!
ERROR - 2021-10-19 11:28:10,105 - process:15144 - test.py - root - line:54 - test - This is log error!
DEBUG - 2021-10-19 11:28:10,105 - process:15144 - test.py - root - line:55 - test - This is log debug!
ERROR - 2021-10-19 11:28:10,105 - process:15144 - test.py - root - line:61 - test - 'gender'
Traceback (most recent call last):File "D:/python3Project/test.py", line 59, in logging_testgender = people_info["gender"]
KeyError: 'gender'

它在控制台中是这样的:

当然,如果你不需要很复杂的功能,希望简洁一点,仅仅需要在控制台输出一下日志的话,也可以只进行简单的配置:

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.getLogger()
【03x00】更优雅的解决方案:Loguru

对于 logging 模块,即便是简单的使用,也需要自己定义格式,这里介绍一个更加优雅、高效、简洁的第三方模块:loguru,官方的介绍是:Loguru is a library which aims to bring enjoyable logging in Python. Loguru 旨在为 Python 带来愉快的日志记录。这里引用官方的一个 GIF 来快速演示其功能:


安装

Loguru 仅支持 Python 3.5 及以上的版本,使用 pip 安装即可:

pip install loguru

开箱即用

Loguru 的主要概念是只有一个:logger

from loguru import loggerlogger.info("This is log info!")
logger.warning("This is log warn!")
logger.error("This is log error!")
logger.debug("This is log debug!")

控制台输出:

可以看到不需要手动设置,Loguru 会提前配置一些基础信息,自动输出时间、日志级别、模块名、行号等信息,而且根据等级的不同,还自动设置了不同的颜色,方便观察,真正做到了开箱即用!


add() / remove()

如果想自定义日志级别,自定义日志格式,保存日志到文件该怎么办?与 logging 模块不同,不需要 Handler,不需要 Formatter,只需要一个 add() 函数就可以了,例如我们想把日志储存到文件:

from loguru import loggerlogger.add('test.log')
logger.debug('this is a debug')

我们不需要像 logging 模块一样再声明一个 FileHandler 了,就一行 add() 语句搞定,运行之后会发现目录下 test.log 里面同样出现了刚刚控制台输出的 debug 信息。

add() 语句相反,remove() 语句可以删除我们添加的配置:

from loguru import loggerlog_file = logger.add('test.log')
logger.debug('This is log debug!')
logger.remove(log_file)
logger.debug('This is another log debug!')

此时控制台会输出两条 debug 信息:

2021-10-19 13:53:36.610 | DEBUG    | __main__:<module>:86 - This is log debug!
2021-10-19 13:53:36.611 | DEBUG    | __main__:<module>:88 - This is another log debug!

而 test.log 日志文件里面只有一条 debug 信息,原因就在于我们在第二条 debug 语句之前使用了 remove() 语句。


完整参数

Loguru 对输出到文件的配置有非常强大的支持,比如支持输出到多个文件,分级别分别输出,过大创建新文件,过久自动删除等等。 下面我们来详细看一下 add() 语句的详细参数:

基本语法:

add(sink, *, level='DEBUG', format='<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>', filter=None, colorize=None, serialize=False, backtrace=True, diagnose=True, enqueue=False, catch=True, **kwargs)

基本参数释义:

  • sink:可以是一个 file 对象,例如 sys.stderropen('file.log', 'w'),也可以是 str 字符串或者 pathlib.Path 对象,即文件路径,也可以是一个方法,可以自行定义输出实现,也可以是一个 logging 模块的 Handler,比如 FileHandler、StreamHandler 等,还可以是 coroutine function,即一个返回协程对象的函数等。
  • level:日志输出和保存级别。
  • format:日志格式模板。
  • filter:一个可选的指令,用于决定每个记录的消息是否应该发送到 sink。
  • colorize:格式化消息中包含的颜色标记是否应转换为用于终端着色的 ansi 代码,或以其他方式剥离。 如果没有,则根据 sink 是否为 tty(电传打字机缩写) 自动做出选择。
  • serialize:在发送到 sink 之前,是否应首先将记录的消息转换为 JSON 字符串。
  • backtrace:格式化的异常跟踪是否应该向上扩展,超出捕获点,以显示生成错误的完整堆栈跟踪。
  • diagnose:异常跟踪是否应显示变量值以简化调试。建议在生产环境中设置 False,避免泄露敏感数据。
  • enqueue:要记录的消息是否应在到达 sink 之前首先通过多进程安全队列,这在通过多个进程记录到文件时很有用,这样做的好处还在于使日志记录调用是非阻塞的。
  • catch:是否应自动捕获 sink 处理日志消息时发生的错误,如果为 True,则会在 sys.stderr 上显示异常消息,但该异常不会传播到 sink,从而防止应用程序崩溃。
  • **kwargs:仅对配置协程或文件接收器有效的附加参数(见下文)。

当且仅当 sink 是协程函数时,以下参数适用:

  • loop:将在其中调度和执行异步日志记录任务的事件循环。如果为 None,将使用 asyncio.get_event_loop() 返回的循环。

当且仅当 sink 是文件路径时,以下参数适用:

  • rotation:一种条件,指示何时应关闭当前记录的文件并开始新的文件。
  • **retention **:过滤旧文件的指令,在循环或程序结束期间会删除旧文件。
  • compression:日志文件在关闭时应转换为的压缩或存档格式。
  • delay:是在配置 sink 后立即创建文件,还是延迟到第一条记录的消息时再创建。默认为 False
  • mode:内置 open() 函数的打开模式,默认为 a(以追加模式打开文件)。
  • buffering:内置 open() 函数的缓冲策略,默认为1(行缓冲文件)。
  • encoding:内置 open() 函数的文件编码,如果 None,则默认为 locale.getpreferredencoding()
  • **kwargs:其他传递给内置 open() 函数的参数。

这么多参数可以见识到 add() 函数的强大之处,仅仅一个函数就能实现 logging 模块的诸多功能,接下来介绍几个比较常用的方法。


rotation 日志文件分隔

add() 函数的 rotation 参数,可以实现按照固定时间创建新的日志文件,比如设置每天 0 点新创建一个 log 文件:

logger.add('runtime_{time}.log', rotation='00:00')

设置超过 500 MB 新创建一个 log 文件:

logger.add('runtime_{time}.log', rotation="500 MB")

设置每隔一个周新创建一个 log 文件:

logger.add('runtime_{time}.log', rotation='1 week')

retention 日志保留时间

add() 函数的 retention 参数,可以设置日志的最长保留时间,比如设置日志文件最长保留 15 天:

logger.add('runtime_{time}.log', retention='15 days')

设置日志文件最多保留 10 个:

logger.add('runtime_{time}.log', retention=10)

也可以是一个 datetime.timedelta 对象,比如设置日志文件最多保留 5 个小时:

import datetime
from loguru import loggerlogger.add('runtime_{time}.log', retention=datetime.timedelta(hours=5))

compression 日志压缩格式

add() 函数的 compression 参数,可以配置日志文件的压缩格式,这样可以更加节省存储空间,比如设置使用 zip 文件格式保存:

logger.add('runtime_{time}.log', compression='zip')

其格式支持:gzbz2xzlzmatartar.gztar.bz2tar.xz


字符串格式化

Loguru 在输出 log 的时候还提供了非常友好的字符串格式化功能,相当于 str.format()

logger.info('If you are using Python {}, prefer {feature} of course!', 3.6, feature='f-strings')

输出:

2021-10-19 14:59:06.412 | INFO     | __main__:<module>:3 - If you are using Python 3.6, prefer f-strings of course!

异常追溯

在 Loguru 里可以直接使用它提供的装饰器就可以直接进行异常捕获,而且得到的日志是无比详细的:

from loguru import logger@logger.catch
def my_function(x, y, z):# An error? It's caught anyway!return 1 / (x + y + z)my_function(0, 0, 0)

日志输出:

2021-10-19 15:04:51.675 | ERROR    | __main__:<module>:10 - An error has been caught in function '<module>', process 'MainProcess' (30456), thread 'MainThread' (26268):
Traceback (most recent call last):> File "D:/python3Project\test.py", line 10, in <module>my_function(0, 0, 0)└ <function my_function at 0x014CDFA8>File "D:/python3Project\test.py", line 7, in my_functionreturn 1 / (x + y + z)│   │   └ 0│   └ 0└ 0ZeroDivisionError: division by zero

在控制台的输出是这样的:

相比 Logging,Loguru 无论是在配置方面、日志输出样式还是异常追踪,都远优于 Logging,使用 Loguru 无疑能提升开发人员效率。本文仅介绍了一些常用的方法,想要详细了解可参考 Loguru 官方文档或关注 Loguru GitHub。


Loguru:Python 日志终极解决方案相关推荐

  1. Python日志详解【两篇就够了系列】--第二篇loguru

    目录 第二章 Python日志loguru库详解 一.loguru简介 二.日志级别 三.loguru日志常用参数配置解析 1.rotation 2.retention 3.compression 4 ...

  2. python 日志解决方案_日常Python问题的绝佳解决方案

    python 日志解决方案 Python提供了一组独特的工具和语言功能,可帮助使您的代码更加优雅,可读性和直观性. 通过为正确的问题选择正确的工具,您的代码将更易于维护. 在本文中,我们将研究其中的三 ...

  3. yum yum doesn‘t match version of Python 终极解决方案

    yum yum doesn't match version of Python 终极解决方案 参考文章: (1)yum yum doesn't match version of Python 终极解决 ...

  4. sublime配置python开发环境以及遇到的坑(附终极解决方案)

    最近一直在写python项目,在此之前我用的工具主要是pycharm,由于我的笔记本配置渣,每次打开pycharm后我都要去倒杯水,然后回来看看有没有打开我的项目,时间充足的时候还好,如果有同事或者领 ...

  5. Python日志库logging、loguru、Eliot

    文章目录 简介 初试 日志基础教程 消息格式 日志属性 信息流程 通过配置文件创建 PyCharm日志插件 封装 loguru入门 Eliot入门 参考文献 简介 logging,Python内置库, ...

  6. python中Matplotlib、seaborn中英文乱码终极解决方案

    关于Matplotlib中文乱码的问题一直困扰着我这个朴朴素素的制图人员,我也是试来试去各种方法无果之后,发现了一个最佳的解决方案.首先我列举一些市面上并不一定有效的解决方案: import matp ...

  7. oracle export utf-8,Linux操作系统下终端乱码的终极解决方案 export LANG=zh_CN.UTF-8 export LANG=en_US...

    在使用linux的终端工具SecureCRT的时候,每次提交SVN想输入中文日志的时候总是输不了中文. svn ci -m "" 这时候两个引号之间就是没有办法输入中文. 后来跟其 ...

  8. 分布式事务终极解决方案探讨

    2019独角兽企业重金招聘Python工程师标准>>> 分布式事务终极解决方案探讨 转载于:https://my.oschina.net/dslcode/blog/1606115

  9. 终极解决方案:Emacs+Slime+Lisp启动错误:Polling /tmp/slime.50

    2019独角兽企业重金招聘Python工程师标准>>> 终极解决方案:Emacs+Slime+Lisp启动错误:Polling "/tmp/slime.5000 .. 25 ...

最新文章

  1. php 本地mysql 代码_基于本地数据库的 IP 地址查询 PHP 源码
  2. 国产手机都会用鸿蒙吗,华为鸿蒙系统已经发布,小米等国产手机会使用鸿蒙系统吗?来看看...
  3. Spring –持久层–编写实体并配置Hibernate
  4. python安全攻防---scapy基础---计算机网络各层协议
  5. mycli一个非常有趣的bug
  6. java hung_java – Hung JVM消耗100%的CPU
  7. python制作的游戏如何转化为swf_PYTHON实现swf提取
  8. Vue 导出excel 导出多个sheet
  9. 2018c语言二级选择题题库,计算机二级office题库选择题及答案
  10. 魔兽世界燃烧的远征最新服务器,魔兽世界燃烧的远征怀旧服
  11. if [ $# -ne 1 ];then 是什么意思?
  12. mysql数据库设计与应用答案智慧树_知到智慧树MySQL数据库设计与应用完整免费答案...
  13. 什么是项目集(PgMP)?
  14. linux网络协议栈(四)链路层 vlan处理
  15. Log4j2 重大漏洞与解决方案
  16. java八大基本数据类型及其封装类
  17. 支付宝小程序状态栏显示图片
  18. 利用朴素贝叶斯分类算法对搜狐新闻进行分类(python)
  19. python学习笔记14 图像格式转换png转jpg
  20. ProximitySensor校准

热门文章

  1. LDA-Latent Dirichlet Allocation 学习笔记
  2. 996. Number of Squareful Arrays
  3. 【数据结构与算法】【算法思想】分治算法
  4. [小技巧][JAVA][转换]整型int与字符char相互转换
  5. 专属海报小程序_剑3泡泡 | 小程序给你一份专属的账号海报!
  6. 计算机网络原理关于实验中几个指令使用的复习——网络层
  7. javaweb开发的准备工作——配置篇
  8. linux ubantu扩展空间,ubuntu 扩展存储空间
  9. AD16原理图.schdot中批量修改标签中的文本字体、大小、颜色
  10. python unicode error_关于GAE中运行python出现unicode decode error