1. 为什么要使用日志(作用)

在学习过程中,写一个小程序或小demo时,遇到程序出错时,我们一般会将一些信息打印到控制台进行查看,方便排查错误。这种方法在较小的程序中很实用,但是当你的程序变大,或是说在实际的工程项目中,代码量都是很大的,代码量一大,不可控因素就多,再将信息打印到控制台进行查看是非常不方便的,此时就需要使用日志来记录程序运行过程中的相关信息,监控程序的运行情况。

2. 基础概念

我们在程序中会使用print打印一些信息以供我们判断程序的运行情况,什么时候需要使用print打印一些信息以及在什么位置使用print,这些都是根据程序自身以及个人的经验。使用日志和使用print是相似的,不同的时,日志具有多种形式来记录一些信息,例如 logging.FileHandler ****将日志信息文件写入文件,logging.handlers.SMTPHandler 将日志信息通过SMTP发送到一个电子邮件地址。

记录和处理日志信息的方式有很多种,logging库中提供了许多方法:https://docs.python.org/zh-cn/3/library/logging.handlers.html# 本文只记录 StreamHandler和 FileHandler 的使用方法。

logging库虽然提供了多种方法来记录和处理日志信息,但并不是什么信息都需要记录的,我们往往会根据程序中各个模块的重要程度来决定是否需要记录信息以及记录哪些信息。logging库根据事件的重要性提供了以下几个级别:

级别 使用时机
DEBUG 细节信息,仅当诊断问题时适用。
INFO 确认程序按预期运行,和print使用类似。
WARNING 表明有已经或即将发生的意外(例如:磁盘空间不足,数值运算产生精度丢失等)。程序仍按预期进行。
ERROR 由于严重的问题,程序的某些功能已经不能正常执行。
CRITICAL 严重的错误,表明程序已不能继续执行。

级别大小:DEBUG < INFO < WARNING <ERROR < CRITICAL

划分处理等级的目的是为了针对不同的情况来记录有用的信息,方便有效地监控程序的运行状况,当程序出错时也能方便的定位到。因此在使用logging库记录和处理日志信息前,需要先设定处理等级,默认的等级为 WARNING,当记录和处理日志信息时,只处理大于等于该等级的日志信息。

3. logging库的一些简单使用案例

3.1 像print一样打印日志信息

使用debug()、info()、warning()、error()、critical() 方法分别打印对应的级别及以上的信息,因为默认的级别为 WARNING,所以只会对warning()、error()、critical() 中的信息进行打印。

import logging

if __name__ == "__main__":
logging.debug("the message of debug")
logging.info("the message of debug")
logging.warning("the message of warning")
logging.error("the message of error")
logging.critical("the message of critical") 输出结果:
WARNING:root:the message of warning
ERROR:root:the message of error
CRITICAL:root:the message of critical

3.2 设置级别

使用 basicConfig() 方法设置级别,传入关键字参数 level ,参数值为对应级别的字符串形式。

import logging

if __name__ =="__main__":
logging.basicConfig(level='DEBUG')
logging.debug("the message of debug")
logging.info("the message of debug")
logging.warning("the message of warning")
logging.error("the message of error")
logging.critical("the message of critical") 输出结果:
DEBUG:root:the message of debug
INFO:root:the message of debug
WARNING:root:the message of warning
ERROR:root:the message of error
CRITICAL:root:the message of critical

3.3 在日志中打印变量信息

默认使用 printf 风格的方式进行字符串格式化。

import logging

if __name__ == "__main__":
name = "张三"
age = 24
logging.warning("%s的年龄为%s岁", name, age)
# 也可以使用format的方式进行字符串格式化
logging.warning("{}的年龄为{}岁".format(name, age)) 输出结果:
WARNING:root:张三的年龄为24岁

3.4 更改日志信息显示的格式

对于可以出现在格式字符串中的全部内容,可以参考官方文档 LogRecord 属性

我们可以将日志信息的显示格式使用关键字 format 设置为:

format='%(asctime)s: %(name)s: %(levelname)s: %(filename)s: %(funcName)s: %(message)s'

记录时间 - 记录器的名称 - 事件等级 - 产生日志信息的源文件名 - 所在函数名 - 提示信息

import logging

def main():
logging.basicConfig(format='%(asctime)s: %(name)s: %(levelname)s: %(filename)s: %(funcName)s: %(message)s',
level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too') if __name__ == "__main__":
main() 输出结果:
2022-04-19 15:30:02,404: root: DEBUG: logging_demo.py: main: This message should appear on the console
2022-04-19 15:30:02,404: root: INFO: logging_demo.py: main: So should this
2022-04-19 15:30:02,404: root: WARNING: logging_demo.py: main: And this, too

3.5 将日志信息写入文件

指定文件名 filename,文件写入方式 filenmode,默认写入方式为 a

import logging

def main():
fmt = '%(asctime)s: %(name)s: %(levelname)s: %(filename)s: %(funcName)s: %(message)s'
logging.basicConfig(filename="example.log", filemode='w', format=fmt, level="DEBUG")
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too') if __name__ == "__main__":
main()

3.6 basicConfig

前面几个案例都使用到了 basicConfig 方法对打印和记录日志信息的行为进行设置,可以设置是将信息显示在控制台还是记录到文件中,可以设置显示或记录的内容格式,设置事件等级等,更详细的设置官方文档已经给出了很细致的解释。

4. 进一步使用

当程序规模变大,使用上面方式介绍的记录日志的方式就会显得捉襟见肘。下面介绍 logging库的进阶使用方式。

logging 库采用模块化的方式来记录日志信息,由以下四个核心组件组成:

Logger(记录器):根据记录器设置的等级捕获日志信息,并传递给对应的处理器;

Handler(处理器):将记录器传递过来的日志信息交给不同的处理器处理;

Filter(过滤器):更细粒度地控制要输出哪些日志记录;

Formatter(格式器):设置日志记录的输出格式;

下面说明这几个组件之间的联系及处理顺序:

  1. 首先通过 getLogger() 方法一个指定名称的 Logger实例的引用,如果没提供名称,则返回 rootlogger = getLogger(name)
  2. 为 Logger 实例设置捕获信息的等级,默认等级为 WARNING,logger.setLevel('INFO')
  3. logger 通过调用 debug()、info()、warning()、error()、critical() 这些方法来捕获日志信息;Logger 只暴露接口供程序员调用,用来捕获日志信息,而不处理信息;只捕获大于等于其自身等级的信息;
  4. 如果 logger 捕获了日志信息,则将日志信息传递给 Handler 进行处理;
  5. 每个 logger 可以有多个 Handler 处理捕获的日志信息,常用的 StreamHandler 和 FileHandler ,StreamHandler 将日志信息打印到终端显示,FileHandler 将日志信息记录写入到文件中;不同的Handler 也都具有等级属性,每个Handler 只会处理大于等于自身等级的日志信息;

其中 Filter 可以作用 Logger 和 Handler,用于更细粒度地控制捕获或输出哪些日志信息,本文不讨论 Filter 使用。

日志事件信息流程如下图所示:

整体代码框架如下:

# 实例化一个 Logger 对象,并记录一个名字
# 在命名记录器时使用的一个好习惯,是在每个使用日志记录的模块中使用模块级记录器
logger = logging.getLogger(__name__)
# 设置记录器捕获日志信息的等级
logger.setLevel("INFO") # 实例化处理器对象
ch = logging.StreamHandler()
fh = logging.FileHandler("example.log") # 设置处理器处理信息的等级
ch.setLevel("INFO")
fh.setLevel("WARNING") # 设置处理器输出日志信息的格式
fmt = logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: %(filename)s: %(message)s')
ch.setFormatter(fmt)
fh.setFormatter(fmt) # 给记录器添加处理器
logger.addHandler(ch)
logger.addHandler(fh) # 捕获日志信息
logger.info("the message info")
logger.error("the message of error")

将上面代码封装成一个类,方便使用

import logging

class Logger:
"""
将日志信息打印到控制台和记录到文件的操作封装成一个类
"""
def __init__(self, name: str, console_handler_level: str = logging.DEBUG,
fmt: str = '%(asctime)s: %(levelname)s: %(name)s: %(filename)s: %(message)s'):
"""
默认会添加一个等级为 'DEBUG' 的 Handler 对象到 Logger 对象
:param name: handler 的名称
:param console_handler_level: 设置 StreamHandler 的等级
:param fmt: 日志消息的显示格式
"""
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)
self.fmt = logging.Formatter(fmt)
self.set_console_handler(console_handler_level) def set_console_handler(self, console_handler_level: str = logging.DEBUG) -> None:
ch = logging.StreamHandler()
ch.setLevel(console_handler_level)
ch.setFormatter(self.fmt)
self.logger.addHandler(ch) def set_file_handler(self, filename: str, mode: str = 'a', file_handler_level: str = logging.INFO) -> None:
fh = logging.FileHandler(filename, mode=mode, encoding='utf-8')
fh.setLevel(file_handler_level)
fh.setFormatter(self.fmt)
self.logger.addHandler(fh) def debug(self, msg):
self.logger.debug(msg) def info(self, msg):
self.logger.info(msg) def warning(self, msg):
self.logger.warning(msg) def error(self, msg):
self.logger.error(msg) def critical(self, msg):
self.logger.critical(msg)

日志(logging模块)的更多相关文章

  1. day18包的使用与日志(logging)模块

    包的使用与日志(logging)模块1. 什么是包    包就是一个包含有__init__.py文件的文件夹    包本质就是一种模块,即包是用包导入使用的,包内部包含的文件也都是用来被导入使用2 为 ...

  2. python中日志logging模块的性能及多进程详解

    python中日志logging模块的性能及多进程详解 使用Python来写后台任务时,时常需要使用输出日志来记录程序运行的状态,并在发生错误时将错误的详细信息保存下来,以别调试和分析.Python的 ...

  3. python 的日志logging模块学习

    1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info messa ...

  4. Python之日志 logging模块

    关于logging模块的日志功能 典型的日志记录的步骤是这样的: 创建logger 创建handler 定义formatter 给handler添加formatter 给logger添加handler ...

  5. python 的日志logging模块

    1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message')logging.info('This is info messag ...

  6. Python日志(logging)模块,shelve,sys模块

    菜鸟学python第十七天 1.logging 模块 logging模块即日志记录模块 用途:用来记录日志 为什么要记录日志: 为了日后复查,提取有用信息 如何记录文件 直接打开文件,往里写东西 直接 ...

  7. python的日志logging模块性能以及多进程

    写在前面: 日志是记录操作的一种好方式.但是日志,基本都是基于文件的,也就是要写到磁盘上的.这时候,磁盘将会成为一个性能瓶颈.对于普通的服务器硬盘(机械磁盘,非固态硬盘),python日志的性能瓶颈是 ...

  8. python+Appium自动化:日志logging模块

    日志级别 debug.info.warn.error.critical五个级别 logging模块构成(四部分) logger(记录器,用于日志采集) Handler(处理器,将日志记录发送到合适的路 ...

  9. Python中日志logging模块

    # coding:utf-8 import logging import os import time class Logger(object): def __init__(self): # 创建一个 ...

  10. Python的日志记录-logging模块的使用

    一.日志 1.1什么是日志 日志是跟踪软件运行时所发生的事件的一种方法,软件开发者在代码中调用日志函数,表明发生了特定的事件,事件由描述性消息描述,同时还包含事件的重要性,重要性也称为级别或严重性. ...

随机推荐

  1. bzoj2928: [Poi1999]飞弹

    惨啊…… 被卡常是一种什么感受&…… 很明显的分治. 我们首先可以找到所有点中的最低点,然后对所有点进行一次极角排序,选取一个点使得他各侧飞弹和地堡一样多,并对两侧继续进行分治. 很容易证明这 ...

  2. IOS 设置导航栏

    //设置导航栏的标题 self.navigationItem setTitle:@"我的标题"; //设置导航条标题属性:字体大小/字体颜色…… /*设置头的属性:setTitle ...

  3. XMLHTTPRequest/Ajax请求 和普通请求的区别

    Ajax请求头会多一个x-requested-with参数,值为XMLHttpRequest 详情:http://blog.csdn.net/zhangdaiscott/article/details ...

  4. Python 列表元素排重uniq

    # -*- coding: gbk -*- def uniq(ls): lsCopy=[e for e in ls] for i in xrange(1,len(ls)): for j in xran ...

  5. [转] ubuntu开启SSH服务

    点击阅读原文 SSH分客户端openssh-client和openssh-server如果你只是想登陆别的机器的SSH只需要安装openssh-client(ubuntu有默认安装,如果没有则sudo ...

  6. mysql 重置root 账户密码

    windows: 打开命令行窗口,停止mysql服务:        Net stop mysql启动mysql,一般到mysql的安装路径,找到 mysqld-nt.exe <<< ...

  7. MT4平台上mql4实现的基于macd指标的智能交易EA

    屌丝命苦,拼爹拼不过,拼后台没有,技术宅一枚,情商有问题,不会见人说人话见鬼说鬼话,所以在国庆熬着混着,工作也没啥大起色,想想就郁闷,难不成一辈子就只能这样了? 苦思冥想,想得一条路,那就是程序化交易 ...

  8. 简易视频播放器2 (基于Qt、opencv)

    因项目需要,需要实现一个对以保存的监测视频快速查看功能. 查询网上一些资料,初步简易的实现了一下. 实际效果图: 该程序基于Qt5.4,opencv248,开发环境为win8.1 结构为: video ...

  9. Android过滤Logcat输出

    logcat和grep配合使用 1.打印特定tag的log,如打印Tag为Adm的Log        adb logcat | grep Adm        adb logcat | grep - ...

  10. winform窗体对象 单例模式与泛型结合

    实现弹出窗体对象的单例模式  结合泛型后,可以用于所有窗体的弹出操作 public class BaseFrm<T> where T : Form, new() { //定义一个静态的,私 ...