赞
踩
参考资料:https://docs.python.org/zh-cn/3/howto/logging.html#logging-basic-tutorial
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。
级别 | levelno | 场景 |
---|---|---|
DEBUG | 10 | 程序调试详细信息 |
INFO | 20 | 按照正常预期执行 |
WARNING | 30 | 告警信息 |
ERROR | 40 | 程序部分模块运行错误 |
CRITICAL | 50 | 整体严重错误,无法继续执行 |
日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的;
命令行方式设置日志级别:
----log=INFO
logging模块的2种日志方式
A:通过 logging提供的模块级别的函数
B:使用Logging日志系统的四大组件
其实,logging所提供的模块级别的日志记录函数也是对logging日志系统相关类的封装。
logging.debug(msg, *args, **kwargs)
logging.info(msg, *args, **kwargs)
logging.warning(msg, *args, **kwargs)
logging.error(msg, *args, **kwargs)
logging.critical(msg, *args, **kwargs)
logging.log(level, *args, **kwargs)
logging.basicConfig(**kwargs)
简单实现
# coding=utf-8
"""
@DevTool : PyCharm
@Author : xxx
@DateTime : 2022/3/29 9:56
@FileName : test_09.py
"""
import logging
logging.debug("there is debug message!")
logging.info("there is info message!")
logging.warning("there is warning message!")
logging.error("there is error message!")
logging.critical("there is critical message!")
输出结果
D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
WARNING:root:there is warning message!
ERROR:root:there is error message!
CRITICAL:root:there is critical message!
Process finished with exit code 0
备注:默认情况下 Python的logging模块只输出日志级别 大于等于 warning
的信息;
在代码开始位置新增如下代码:
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s: %(message)s", datefmt = "%Y-%m-%d %H:%M:%S")
输出结果:
D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
2022-04-08 16:01:41 DEBUG: there is debug message!
2022-04-08 16:01:41 INFO: there is info message!
2022-04-08 16:01:41 WARNING: there is warning message!
2022-04-08 16:01:41 ERROR: there is error message!
2022-04-08 16:01:41 CRITICAL: there is critical message!
Process finished with exit code 0
logging.debug()、logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数,其支持3个关键字参数: exc_info, stack_info, extra,具体用法如下:
其值为布尔值,如果该参数的值设置为True,则会将异常异常信息添加到日志消息中。如果没有异常信息则添加None到日志信息中。
其值也为布尔值,默认值为False。如果该参数的值设置为True,栈信息将会被添加到日志信息中。
这是一个字典(dict)参数,它可以用来自定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突。
具体用法
# coding=utf-8 """ @DevTool : PyCharm @Author : xxx @DateTime : 2022/3/29 9:56 @FileName : test_09.py """ import logging dict_extra={"user":"robot","ip":"99.99.99.99"} logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s {%(user)s-%(ip)s}: %(message)s", datefmt = "%Y-%m-%d %H:%M:%S") logging.info("there is info message!",exc_info=True,stack_info=True,extra=dict_extra) logging.warning("there is warning message!",exc_info=True,stack_info=False,extra=dict_extra) logging.error("there is error message!",exc_info=False,stack_info=False,extra=dict_extra)
结果输出
D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
2022-05-02 21:15:53 INFO {robot-99.99.99.99}: there is info message!
NoneType: None
Stack (most recent call last):
File "E:/PythonProject/FileOperTest/test_09.py", line 14, in <module>
logging.info("there is info message!",exc_info=True,stack_info=True,extra=dict_extra)
2022-05-02 21:15:53 WARNING {robot-99.99.99.99}: there is warning message!
NoneType: None
2022-05-02 21:15:53 ERROR {robot-99.99.99.99}: there is error message!
Process finished with exit code 0
参数 | 描述 |
---|---|
filename | 指定日志输出到的目标文件的文件名称 |
filemode | 指定日志文件的打开模式,默认为’a’;该选项要在filename指定时才有效; |
format | 指定日志格式字符串,即指定日志输出时所包含的字段信息以及顺序; |
datefmt | 指定日期/时间格式,该选项要在format中包含时间字段%(asctime)s时才有效; |
level | 指定日志器的日志级别; |
stream | 指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream;stream和filename不能同时指定,否则会引发 ValueError异常; |
style | 指定format格式字符串的风格,可取值为’%‘、’{‘和’$‘,默认为’%'; |
handlers | 这些handler将会被添加到root logger;注意 filename、stream和handlers这三个配置项只能有一个存在,否则会引发ValueError异常; |
属性 | 引用格式 | 描述 |
---|---|---|
asctime | %(asctime)s | 记录日志发生时间,可以精确到毫秒 |
name | %(name)s | 日志器名称,默认是’root’,因为默认使用的是 rootLogger |
filename | %(filename)s | 调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀 |
funcName | %(funcName)s | 调用日志输出函数的函数名 |
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中产生的毫秒部分,可以用这个加上 |
到此为止,Python中简单的日志记录已经可以实现,不过对于更加复杂的场景日志记录则需要如下的日志系统来完成;
logging日志模块四大组件
组件名称 | 对应类名 | 功能描述 |
---|---|---|
日志器 | Logger | 提供了应用程序可一直使用的接口 |
处理器 | Handler | 将logger创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
组件关系
logger 对象
logging.getLogger([name])
返回一个logger对象,如果没有指定名字,则 name 的值为 “root”;若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。
Logger对象最常用的方法分为两类:配置方法 和 消息发送方法;
常用配置方法:
方法 | 描述 |
---|---|
Logger.setLevel() | 设置日志器将会处理的日志消息的最低严重级别 |
Logger.addHandler() 和 Logger.removeHandler() | 为该logger对象添加 和 移除一个handler对象 |
Logger.addFilter() 和 Logger.removeFilter() | 为该logger对象添加 和 移除一个filter对象 |
日志记录方法:
方法 | 描述 |
---|---|
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() | 创建一个与它们的方法名对应等级的日志记录 |
Logger.exception() | 创建一个类似于Logger.error()的日志消息 |
Logger.log() | 需要获取一个明确的日志level参数来创建一个日志记录 |
logger的层级结构和有效等级
Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加零个或者多个handler对象。
因为Handler是一个基类,它只定义了所有handlers都应该有的接口,所以无法直接实例化和使用Handler实例。
常用Handler
:
Handler | 描述 |
---|---|
logging.StreamHandler | 将日志消息输出到Stream,如std.out, std.err或任何file-like对象。 |
logging.FileHandler | 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handlers.HTTPHandler | 将日志消息以GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler | 将日志消息发送给一个指定的email地址 |
logging.NullHandler | 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。 |
handler常用方法
方法 | 描述 |
---|---|
Handler.setLevel() | 设置handler将会处理的日志消息的最低严重级别 |
Handler.setFormatter() | 为handler设置一个格式器对象 |
Handler.addFilter() 和 Handler.removeFilter() | 为handler添加 和 删除一个过滤器对象 |
class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None)
当前被写入的文件总是 app.log。
如果 maxBytes 或 backupCount 两者之一的值为零,日志文件不会发生轮换;
当 backupCount 为 5 而基本文件名为 app.log 时,你将得到 app.log、app.log.1、app.log.2 直至 app.log.5。
class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, errors=None)
日志配置
[handler_timedRotatingFileHandler]
class=logging.handlers.TimedRotatingFileHandler
level=INFO
formatter=commonFormatter
args=("logTrack.log","D",1,180,"utf-8")
说明:此处因为报错TimedRotatingFileHandler 没有定义,所以使用全路径来描述类名称;
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。
类构造方法:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
一般直接用 logging.Formatter(fmt, datefmt)
对象获取
Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。
1、创建一个根 logger对象;
2、设置根 logger对象的日志等级;
3、创建需要的Handler(FileHandler要有路径);
4、设置下每个Handler对象的日志等级;
5、创建下日志的fmt格式和 datefmt格式;
6、设置每个 Handler对象的 formatter 的格式
7、将每个 Handler对象添加到logger对象中;
8、打印输出logger.debug\logger.info\logger.warning\logger.error\logger.critical日志信息;
代码实现
功能描述:按照不同的日志级别,将日志信息分别记录到不同的日志文件;
# coding=utf-8 """ @DevTool : PyCharm @Author : xxx @DateTime : 2022/5/2 0:34 @FileName : test_14.py """ import logging logger = logging.getLogger("Tracker") logger.setLevel(logging.DEBUG) allHandler = logging.FileHandler("all_log.txt",mode="a+",encoding="utf-8") allHandler.setLevel(logging.DEBUG) warnHandler = logging.FileHandler("warn_log.txt",mode="a+",encoding="utf-8") warnHandler.setLevel(logging.WARNING) errorHandler = logging.FileHandler("error_log.txt",mode="a+",encoding="utf-8") errorHandler.setLevel(logging.ERROR) criticalHandler = logging.FileHandler("critical_log.txt",mode="a+",encoding="utf-8") criticalHandler.setLevel(logging.CRITICAL) formatter = logging.Formatter(fmt="%(asctime)s {%(name)s}-[%(levelname)s] %(message)s",datefmt = "%Y-%m-%d %H:%M:%S") # set formatter allHandler.setFormatter(formatter) warnHandler.setFormatter(formatter) errorHandler.setFormatter(formatter) criticalHandler.setFormatter(formatter) # add handlers logger.addHandler(allHandler) logger.addHandler(warnHandler) logger.addHandler(errorHandler) logger.addHandler(criticalHandler) # write log messages logger.debug("there is debug message!") logger.info("there is info message!") logger.warning('{} is {} years old.'.format('Tom', 10)) logger.error("there is error message!") logger.critical("there is critical message!")
all_log.txt
2022-05-02 00:59:13 {Tracker}-[DEBUG] there is debug message!
2022-05-02 00:59:13 {Tracker}-[INFO] there is info message!
2022-05-02 00:59:13 {Tracker}-[WARNING] Tom is 10 years old.
2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!
warn_log.txt
2022-05-02 00:59:13 {Tracker}-[WARNING] Tom is 10 years old.
2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!
error_log.txt
2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!
critical_log.txt
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!
当重复调用一个 同名的 logger 时,将重复 添加相同 Handler,导致日志的重复记录;
解决方式,即在添加之前 判断 logger 的 handlers 是否为空;
实现代码
# coding=utf-8 """ @DevTool : PyCharm @Author : xxx @DateTime : 2022/5/2 0:34 @FileName : test_14.py """ import logging def getConfigedLogger(): logger = logging.getLogger("Tracker") logger.setLevel(logging.DEBUG) warnHandler = logging.FileHandler("warn_log.txt", mode="a+", encoding="utf-8") warnHandler.setLevel(logging.WARNING) errorHandler = logging.FileHandler("error_log.txt", mode="a+", encoding="utf-8") errorHandler.setLevel(logging.ERROR) formatter = logging.Formatter(fmt="%(asctime)s {%(name)s}-[%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") # set formatter warnHandler.setFormatter(formatter) errorHandler.setFormatter(formatter) if not logger.handlers: logger.addHandler(warnHandler) logger.addHandler(errorHandler) return logger realLogger = getConfigedLogger() # write log messages realLogger.debug("there is debug message!") realLogger.info("there is info message!") realLogger.warning('{} is {} years old.'.format('Tom', 10)) realLogger.error("there is error message!") realLogger.critical("there is critical message!")
Python日志常用的3种配置方式如下:
1、使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;
2、创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
3、创建一个包含配置信息的dict,然后把它传递个dictConfig()函数;
参考资料:https://www.cnblogs.com/yyds/p/6885182.html
通过配置文件实现日志记录,实现了配置和代码的分离,同时开发者可以轻松修改日志记录属性;
[loggers] keys=root,tracker [handlers] keys=consoleHandler,fileHandler [formatters] keys=commonFormatter [logger_root] level=INFO handlers=consoleHandler [logger_tracker] level=INFO handlers=consoleHandler,fileHandler qualname=tracker propagate=0 [handler_consoleHandler] class=StreamHandler level=INFO formatter=commonFormatter args=(sys.stdout,) [handler_fileHandler] class=FileHandler level=INFO formatter=commonFormatter args=("logTrack.log","a+","utf-8") [formatter_commonFormatter] format=%(asctime)s -[%(name)s-%(levelname)s]: %(message)s datefmt=%Y-%m-%d %H:%M:%S
当使用配置文件进行日志的记录时,有些关键规则:
0)在配置文件中,首先包含了三大主要模块,loggers、handlers、formatters。对于三个主要模块其包含的内容都是通过keys进行指定,然后通过logger_key/handler_key/formatter_key对里面的key进行具体的设置。
1)配置logger信息,必须包含一个名字叫做root的logger,当使用无参函数logging.getLogger()时,默认返回root这个logger;其他自定义logger可以通过 logging.getLogger(“loggername”) 方式进行调用;
2)对loggers中声明的logger进行逐个配置,且必须指定level和handlers这两个选项;对于非roothandler,还需要添加一些额外的option,其中qualname表示它在logger层级中的名字;
handlers可以指定多个,中间用逗号隔开,比如handlers=fileHandler,consoleHandler,同时制定使用控制台和文件输出日志;
3)在handler中,必须指定class和args这两个option,常用的class包括 StreamHandler、FileHandler、RotaRotatingFileHandler;
args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;里面指定输出路径,比如输出的文件名称等。
level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('Tracker')
除了传递给日志记录函数的参数外,有时候需要日志输出中包含一些额外的上下文信息,如客户端的IP地址和用户信息等,实现方式如下:
参考资料:https://www.cnblogs.com/yyds/p/6897964.html
tree /f
显示目录结构E:.
│ appprog.py
│
├─comTool
│ comFun.py
│
├─conf
│ logging.conf
│
├─logs
│ logTrack.log
│
└─__pycache__
logging.conf 日志配置文件内容:
[loggers] keys=root,tracker # 和上面部分一样 ... [handler_fileHandler] class=FileHandler level=INFO formatter=commonFormatter args=("./logs/logTrack.log","a+","utf-8") [formatter_commonFormatter] format=[%(asctime)s]-[%(name)s-%(levelname)s]-->[%(filename)s]: %(message)s datefmt=%Y-%m-%d %H:%M:%S
主程序 appprog.py 内容如下:
# -*- coding= utf-8 -*- """ @DevTool : PyCharm @Author : xxx @DateTime : 2023/6/15 13:41 @FileName : appprog.py """ import logging import logging.config from comTool import comFun if __name__ == "__main__": print("the main app is running!") logging.config.fileConfig('./conf/logging.conf') logger = logging.getLogger("tracker") logger.info("the main app is running!") comFun.recordTest()
公共程序 comFun.py 内容如下:
# -*- coding= utf-8 -*-
"""
@DevTool : PyCharm
@Author : xxx
@DateTime : 2023/6/15 14:12
@FileName : comFun.py
"""
import logging
def recordTest():
print("current step is in function recordTest!")
logger = logging.getLogger("tracker")
logger.info("current step is in function recordTest!")
在公共程序中,logger 可以直接使用在主程序配置的;
控制台输出内容:
the main app is running!
[2023-06-15 14:48:58]-[tracker-INFO]-->[appprog.py]: the main app is running!
current step is in function recordTest!
[2023-06-15 14:48:58]-[tracker-INFO]-->[comFun.py]: current step is in function recordTest!
Process finished with exit code 0
日志输出内容:
[2023-06-15 14:40:43]-[tracker-INFO]-->[appprog.py]: the main app is running!
[2023-06-15 14:40:43]-[tracker-INFO]-->[comTool.py]: current step is in function recordTest!
================================ over ========================================
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。