python之 logging 模块(上篇)

一、日志关概念

日志是一种可以追踪某些软件运行时所发生事件的方法。软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情。一个事件可以用一个可包含可选变量数据的消息来描述。此外,事件也有重要性的概念,这个重要性也可以被称为严重性级别(level)。
日志的作用
通过log的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。

简单来讲就是,我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,也可以在应用程序出现故障时快速定位问题。比如,做运维的同学,在接收到报警或各种问题反馈后,进行问题排查时通常都会先去看各种日志,大部分问题都可以在日志中找到答案。再比如,做开发的同学,可以通过IDE控制台上输出的各种日志进行程序调试。对于运维老司机或者有经验的开发人员,可以快速的通过日志定位到问题的根源。可见,日志的重要性不可小觑。日志的作用可以简单总结为以下3点:
程序调试

了解软件程序运行情况,是否正常

软件程序运行故障分析与问题定位

如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析,如:分析用户的操作行为、类型洗好、地域分布以及其它更多的信息,由此可以实现改进业务、提高商业利益。
日志的等级
我们先来思考下下面的两个问题:

作为开发人员,在开发一个应用程序时需要什么日志信息?在应用程序正式上线后需要什么日志信息?

作为应用运维人员,在部署开发环境时需要什么日志信息?在部署生产环境时需要什么日志信息?

在软件开发阶段或部署开发环境时,为了尽可能详细的查看应用程序的运行状态来保证上线后的稳定性,我们可能需要把该应用程序所有的运行日志全部记录下来进行分析,这是非常耗费机器性能的。当应用程序正式发布或在生产环境部署应用程序时,我们通常只需要记录应用程序的异常信息、错误信息等,这样既可以减小服务器的I/O压力,也可以避免我们在排查故障时被淹没在日志的海洋里。那么,怎样才能在不改动应用程序代码的情况下实现在不同的环境记录不同详细程度的日志呢?这就是日志等级的作用了,我们通过配置文件指定我们需要的日志等级就可以了。
不同的应用程序所定义的日志等级可能会有所差别,分的详细点的会包含以下几个等级:

DEBUG

INFO

NOTICE

WARNING

ERROR

CRITICAL

ALERT

EMERGENCY

级别	何时使用
DEBUG	详细信息,典型地调试问题时会感兴趣。 详细的debug信息。
INFO	证明事情按预期工作。 关键事件。
WARNING	表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。
ERROR	由于更严重的问题,软件已不能执行一些功能了。 一般错误消息。
CRITICAL	严重错误,表明软件已不能继续运行了。
NOTICE	不是错误,但是可能需要处理。普通但是重要的事件。
ALERT	需要立即修复,例如系统数据库损坏。
EMERGENCY	紧急情况,系统不可用(例如系统崩溃),一般会通知所有用户。
3、日志字段信息与日志格式
一条日志信息对应的是一个事件的发生,而一个事件通常需要包括以下几个内容:

事件发生时间

事件发生位置

事件的严重程度–日志级别

事件内容

上面这些都是一条日志记录中可能包含的字段信息,当然还可以包括一些其他信息,如进程ID、进程名称、线程ID、线程名称等。日志格式就是用来定义一条日志记录中包含那些字段的,且日志格式通常都是可以自定义的。
4、日志功能的实现
几乎所有开发语言都会内置日志相关功能,或者会有比较优秀的第三方库来提供日志操作功能,比如:log4j,log4php等。它们功能强大、使用简单。Python自身也提供了一个用于记录日志的标准库模块–logging。

二、 logging 模块介绍

什么是logging模块
logging模块是python自带的标准模块
logging模块的作用
主要用于输出运行日志

可以控制输出日志的等级, 过滤一些重要信息, 不显示大量无关要紧的调试信息

日志保存的路径, 可以是输出到终端, 也可以是输出到文件

以及文件轮转等等, 日志文件轮转指的是设置保存日志文件个数, 当超过最大日志文件个数, 最早的那个日志文件会被删除
logging模块的优点:
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:

可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息;

print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出。
logging模块日志的级别
logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱。

日志等级(level)	描述
DEBUG	最详细的日志信息,典型应用场景是 问题诊断
INFO	信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING	当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR	由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL	当发生严重错误,导致应用程序不能继续运行时记录的信息
CRITICAL = 50 #FATAL = CRITICAL # 紧急
ERROR = 40  # 错误
WARNING = 30 #WARN = WARNING    # 警告
INFO = 20   # 消息
DEBUG = 10  # 调试
NOTSET = 0  # 不设置
开发应用程序或部署开发环境时,可以使用DEBUG或INFO级别的日志获取尽可能详细的日志信息来进行开发或部署调试;

应用上线或部署生产环境时,应该使用WARNING或ERROR或CRITICAL级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。日志级别的指定通常都是在应用程序的配置文件中进行指定的。
说明:

上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的;

当为某个应用程序指定一个日志级别后,应用程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息,nginx、php等应用程序以及这里的python的logging模块都是这样的。同样,logging模块也可以指定日志记录器的日志级别,只有级别大于或等于该指定日志级别的日志记录才会被输出,小于该等级的日志记录将会被丢弃。

三、logging 模块的使用方式介绍
logging使用方式介绍:
logging模块提供了两种记录日志的方式:

第一种方式是使用logging提供的模块级别的函数

第二种方式是使用Logging日志系统的四大组件

其实,logging所提供的模块级别的日志记录函数也是对logging日志系统相关类的封装而已。
示例:直接导入logging模块
import logging
​
# 先进行日志的基本配置
logging.basicConfig(
            # filename='access.log',          # 日志名字 (不指定默认输出到终端)
            format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s', # 日志格式
            datefmt='%Y-%m-%d %H:%M:%S %p',   # 时间格式
            level=30,                         # 日志等级
            )
​
# 进行日志输出
logging.debug('在大楼使用电子设备')          # 10  调试信息
logging.info('大楼里面使用打火机')           # 20  正常运行信息
logging.warning('大楼里抽烟')               # 30 警告  可能出错
logging.error('正在大楼里玩火')             # 40 出错
logging.critical('拿着手榴弹在大楼里溜达')   # 50 出错长时间不管会崩溃
输出结果
2020-12-11 19:50:30 PM - root - WARNING - test: 大楼里抽烟
2020-12-11 19:50:30 PM - root - ERROR - test: 正在大楼里玩火
2020-12-11 19:50:30 PM - root - CRITICAL - test: 拿着手榴弹在大楼里溜达

# 通过日志等级过滤掉了"debug"以及"info"的日志信息 (大于以及等于你设置的那个等级才会输出)
#注意: 打开文件会发生乱码问题,它的内部运行原理可以理解为就是使用with open()打开文件且默认没有指定字符编码, 默认使用的是操作系统的GBK字符编码写入硬盘, 这个时候我们用文本编辑器打开"access.log"文件我们要使用GBK编码的方式来读。
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进行一次性配置
其中logging.basicConfig(**kwargs)函数用于指定“要记录的日志级别”、“日志格式”、“日志输出位置”、“日志文件的打开模式”等信息,其他几个都是用于记录各个级别日志的函数。
第一种使用方式:简单配置
import logging
logging.debug("debug_msg")
logging.info("info_msg")
logging.warning("warning_msg")
logging.error("error_msg")
logging.critical("critical_msg")
​
'''输出结果
WARNING:root:warning_msg
ERROR:root:error_msg
CRITICAL:root:critical_msg
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG)
默认输出格式为
  默认的日志格式为日志级别:Logger名称:用户输出消息
默认日志级别为warning,默认打印到终端
import logging
​
logging.debug('调试debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('错误error')
logging.critical('严重critical')
​

WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
这里可以用 logging.basicConfig()函数调整日志级别、输出格式等
简单示例:
import logging
logging.basicConfig(level=logging.DEBUG,
                    format="%(asctime)s %(name)s %(levelname)s %(message)s",
                    datefmt = '%Y-%m-%d  %H:%M:%S %a'    #注意月份和天数不要搞乱了,这里的格式化符与time模块相同
                    )
logging.debug("msg1")
logging.info("msg2")
logging.warning("msg3")
logging.error("msg4")
logging.critical("msg5")
​
'''输出内容
2020-12-13  23:24:21 Sun root DEBUG msg1
2020-12-13  23:24:21 Sun root INFO msg2
2020-12-13  23:24:21 Sun root WARNING msg3
2020-12-13  23:24:21 Sun root ERROR msg4
2020-12-13  23:24:21 Sun root CRITICAL msg5
logging.basicConfig()函数包含参数说明
参数名称	描述
filename	指定日志输出目标文件的文件名(可以写文件名也可以写文件的完整的绝对路径,写文件名日志放执行文件目录下,写完整路径按照完整路径生成日志文件),指定该设置项后日志信心就不会被输出到控制台了
filemode	指定日志文件的打开模式,默认为’a’。需要注意的是,该选项要在filename指定时才有效
format	指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
datefmt	指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
level	指定日志器的日志级别
stream	指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
style	Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为’%’、'{‘和’$’,默认为’%’
handlers	Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
logging模块中定义好的可以用于format格式字符串说明
字段/属性名称	使用格式	描述
asctime	%(asctime)s	将日志的时间构造成可读的形式,默认情况下是‘2016-02-08 12:00:00,123’精确到毫秒
name	%(name)s	所使用的日志器名称,默认是’root’,因为默认使用的是 rootLogger
filename	%(filename)s	调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
funcName	%(funcName)s	由哪个function发出的log, 调用日志输出函数的函数名
levelname	%(levelname)s	日志的最终等级(被filter修改后的)
message	%(message)s	日志信息, 日志记录的文本内容
lineno	%(lineno)d	当前日志的行号, 调用日志输出函数的语句所在的代码行
levelno	%(levelno)s	该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
pathname	%(pathname)s	完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
process	%(process)s	当前进程, 进程ID。可能没有
processName	%(processName)s	进程名称,Python 3.1新增
thread	%(thread)s	当前线程, 线程ID。可能没有
threadName	%(thread)s	线程名称
module	%(module)s	调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名
created	%(created)f	当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间–时间戳,就是当时调用time.time()函数返回的值
relativeCreated	%(relativeCreated)d	输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数
msecs	%(msecs)d	日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上
升级版日志示例:
import logging
LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s "#配置输出日志格式
DATE_FORMAT = '%Y-%m-%d  %H:%M:%S %a ' #配置输出时间的格式,注意月份和天数不要搞乱了
logging.basicConfig(level=logging.DEBUG,
                    format=LOG_FORMAT,
                    datefmt = DATE_FORMAT ,
                    filename=r"F:python_16day 11	est.log" #有了filename参数就不会直接输出显示到控制台,而是直接写入文件
                    )
logging.debug("msg1")
logging.info("msg2")
logging.warning("msg3")
logging.error("msg4")
logging.critical("msg5")
​
'''输出结果
​
2020-12-13  23:35:22 Sun  root DEBUG F:/python_16/day 11/stupid kid.py msg1 
2020-12-13  23:35:22 Sun  root INFO F:/python_16/day 11/stupid kid.py msg2 
2020-12-13  23:35:22 Sun  root WARNING F:/python_16/day 11/stupid kid.py msg3 
2020-12-13  23:35:22 Sun  root ERROR F:/python_16/day 11/stupid kid.py msg4 
2020-12-13  23:35:22 Sun  root CRITICAL F:/python_16/day 11/stupid kid.py msg5 
​
说明:

logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。

日志器(Logger)是有层级关系的,上面调用的logging模块级别的函数所使用的日志器是RootLogger类的实例,其名称为’root’,它是处于日志器层级关系最顶层的日志器,且该实例是以单例模式存在的。

如果要记录的日志中包含变量数据,可使用一个格式字符串作为这个事件的描述消息(logging.debug、logging.info等函数的第一个参数),然后将变量数据作为第二个参数*args的值进行传递,

如:

logging.warning('%s is %d years old.', 'Tom', 10),
​
'''输出内容
WARNING:root:Tom is 10 years old.
'''
logging.debug(), logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数。它们支持3个关键字参数: exc_info, stack_info, extra,下面对这几个关键字参数作个说明。关于exc_info, stack_info, extra关键词参数的说明:见参考资料(了解)戳我
原文地址:https://www.cnblogs.com/qiukangle/p/14146137.html