Python 标准库 Logging 学习笔记
Python 标准库 Logging 学习笔记
logging 库日志的级别
级别有 5 个,分别是DEBUG INFO WARNING ERROR CRITICAL
对应的如下
DEBUG 级别:10 详细信息,常用于调试
INFO 级别:20 程序正常运行过程中产生的一些信息
WARNING 级别:30 警告用户,虽然程序还能正常工作,但有可能发生错误
ERROR 级别:40 用于更严重的问题,程序已不能执行一些功能了
CRITICAL 级别:50 严重错误,程序已不能继续运行
常规使用
logging 模块提供了basicConfig方法可以进行简单的配置
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
这里面的参数还挺多的,具体可以查阅模块方法。主要就是下面这几个方法。filename、filemode 、format、 datefmt、 style、 level 、stream 、handlers 、force 、 encoding 、 errors。
高级应用
logging模块采用了模块化设计,主要包含四种组件。
Loggers 记录器
提供应用程序代码能直接使用的接口
Handlers 处理器
将记录器产生的日志发送至目的地。可以是文件、标准输出、邮件、或通过 socket、http 等协议发送到任何地方。
StreamHandler
标准输出stdout(如显示器)分发器。
创建方法
sh = logging.StreamHandler(stream=None)
FileHandler
将日志保存到磁盘文件的处理器。
创建方法
fh = logging.FileHandler(filename,mode='a',encoding=None,delay=False)
setFormatter() 设置当前handler对象使用的消息格式。
StreamHandler 常用
FileHandler 常用
BaseRotatingHandler
RotatingFileHandler 常用 按照文件大小 多文件、多日志输出
TimedRotatingFileHandler 按照时间输出 多文件、多日志输出
SocketHandler
DatagramHandler
SMTPHandler
SysLogHandler
NTEventLogHandler
WatchedFileHandler
QueueHandler
NullHandler
Filters 过滤器
提供更好的粒度控制,决定那些日志会被输出
Formatters 格式化器
设置日志内容的组成结构和消息字段。日志信息的顺序、结构。
构造方法如下
tf = logging.Formatter.__init__(fmt=None,datefmt=None,style='$')
datefmt默认是%Y-%m-%d %H:%M:%S格式,style参数默认为百分号,这表示%(<dictionary key>)s格式的字符串。
具体参数
asctime %(asctime)s 日志产生的时间
created %(created)s time.time()生成的日志创建时间戳
filename %(filename)s 生成日志的程序名
funcName %(funcName)s 调用日志的函数名
levelname %(levelname)s 日志级别
levelno %(levelno)s 日志级别对应的数值
lineno %(lineno)s 日志所针对的代码行号
module %(module)s 生成日志的模块名
msecs %(msecs)s 日志生成时间的毫秒部分
message %(message)s 具体的日志信息
name %(name)s 日志的调用者
pathname %(pathname)s 生成日志的文件的完整路径
process %(process)d 生成日志的进程 ID
processName %(processName)s 进程名
thread %(thread)d 生成日志的线程 ID
threadName %(threadName)s 线程名
使用高级应用在穿起来
既然看了高级的使用方法,那么我们也要集合一个案例来用起来,这样会比较容易理解。 现在的背景如下
1、创建一个记录器,用于记录日志;
2、创建一个处理器,用于展示日志,将日志的DEBUG级别和以上输出展示到
3、创建一个处理器,用于记录日志,将日志存储在 addDemo.log 文件中
4、设置好日志的输出格式,确保记录、输出用一套格式
代码如下
import logging
# 创建记录器
logger = logging.getLogger()
# 设置日志级别
logger.setLevel(logging.DEBUG)
# 处理器handler
consoleHandler = logging.StreamHandler()
# 设置日志级别
consoleHandler.setLevel(logging.DEBUG)
# 没有给handler指定日志级别,将使用logger的级别
fileHandler = logging.FileHandler(filename="addDemo.log")
fileHandler.setLevel(logging.INFO)
# formatter 格式
formatter = logging.Formatter(
"%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)s | %(message)s"
)
# 将配置好的串起来
# 给处理器设置格式
consoleHandler.setFormatter(formatter)
fileHandler.setFormatter(formatter)
# 记录器要设置处理器
logger.addHandler(consoleHandler)
logger.addHandler(fileHandler)
# 打印日志的代码
logger.debug("This is a debug text")
logger.info("This is a info text")
logger.warning("This is a warning text")
logger.error("This is a error text")
logger.critical("This is a critical text")
使用配置文件来配置日志
除了使用像上面那种比较繁琐的配置方式来配置日志记录,我们也可以采用conf的外置文件来进行配置,这样代码更加简洁,同时配置相关也可以直接依赖于修改配置文件即可实现。
在将上面的代码继续改造
# logging.conf
[loggers]
keys=root,applog
[handlers]
keys=fileHandler,consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_applog]
level=DEBUG
handlers=fileHandler,consoleHandler
qualname=applog
propagate=0
[handler_consoleHandler]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=simpleFormatter
[handler_fileHandler]
class=handlers.TimedRotatingFileHandler
args=('applog.log','midnight',1,0)
level=DEBUG
formatter=simpleFormatter
[formatter_simpleFormatter]
format=%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)s | %(message)s
datefmt=%Y-%m-%d %H:%M:%S
import logging
import logging.config
# 配置文件的方式来处理日志
# 指定要加载的conf文件
logging.config.fileConfig("logging.conf")
rootLogger = logging.getLogger()
rootLogger.debug("This is root Logger")
logger = logging.getLogger("applog")
# 打印日志的代码
logger.debug("This is a debug text")
a = "abc"
try:
int(a)
except Exception as e:
# logger.error(e)
# 记录打印程序的错误栈信息
logger.exception(e)
使用字典方式来配置日志格式 (推荐)
这种方式在我来看是目前最适合python使用的。因为他可以使用json、xml、数据库存储等任何能够转换成python字典的方式存储都是可以的。还是上面的例子,我原封不动的切换为使用字典方式来配置。
import logging
import logging.config
logging.config.dictConfig({
"loggers":"root,applog",
"handlers":"fileHandler,consoleHandler",
"formatters":"simpleFormatter",
"logger_root":{
"level":"DEBUG",
"handlers":"consoleHandler"
},
"logger_applog":{
"level":"DEBUG",
"handlers":"fileHandler,consoleHandler",
"qualname":"applog",
"propagate":0
},
"handler_consoleHandler":{
"class":"StreamHandler",
"args":"(sys.stdout,)",
"level":"DEBUG",
"formatter":"simpleFormatter"
},
"handler_fileHandler":{
"class":"handlers.TimedRotatingFileHandler",
"args":"('applog.log','midnight',1,0)",
"level":"DEBUG",
"formatter":"simpleFormatter"
},
"formatter_simpleFormatter":{
"format":"%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)s | %(message)s",
"datefmt":"%Y-%m-%d %H:%M:%S"
}
})
rootLogger = logging.getLogger()
rootLogger.debug("This is root Logger")
logger = logging.getLogger("applog")
# 打印日志的代码
logger.debug("This is a debug text")
a = "abc"
try:
int(a)
except Exception as e:
# logger.error(e)
# 记录打印程序的错误栈信息
logger.exception(e)
关于在FastAPI中使用
其实关于使用的方法还挺多的,网上基本一查一大堆。不过很多东西其实不去看,根本不清楚其中的原理。推荐一个我目前在用的方法。
from fastapi import FastAPI
import logging
import logging.config
app = FastAPI()
LOGGING_CONFIG = {
"version": 1,
"formatters": {
"default": {
"format": "%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)s | %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "default",
},
"files": {
"class": "logging.handlers.TimedRotatingFileHandler",
"when": "midnight",
"interval":1,
"formatter": "default",
"filename": "./logs/running.log",
"backupCount": 5,
"encoding": "utf8",
},
},
"disable_existing_loggers": True,
"root": {"handlers": ["console", "files"], "level": "DEBUG"},
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("root")
@app.get("/")
async def main():
logger.error("call")
logger.warn("call")
logger.info("call")
logger.debug("call")
return {"hello": "world!"}
关于上面TimedRotatingFileHandler里面的when参数:
S 一秒一份日志(基本不用)
M 一分钟一份日志(基本不用)
H 一小时一份日志 (用的不多,根据项目量级决定)
D 一天一份日志 (常用)
W 一周一份日志 (常用)
midnight 午夜0点(最常用)
当when设置为S、M、H、D时,是以程序运行的起始时间为基础的,并不是整时、整分、整秒,所以更加推荐使用midnight作为when的参数。
interval:一个整数,when所表示的间隔×interval的值就是文件另存的间隔时间,例如when='midnight',interval=3,就表示每经过3次午夜0点,就会将现有日志文件另存为一个新的文件;
而其他FastAPI文件中使用也非常简单。只要引入一下,就可以随意使用啦~
import logging
logger = logging.getLogger()
最终日志样式如下
2023-07-11 22:26:43 | DEBUG | selector_events.py:59 | Using selector: KqueueSelector
2023-07-11 22:26:44 | DEBUG | event.py:13 | 启动
2023-07-11 22:26:57 | DEBUG | event.py:24 | 站点进程停止.
2023-07-11 22:26:59 | DEBUG | selector_events.py:59 | Using selector: KqueueSelector
2023-07-11 22:26:59 | DEBUG | event.py:13 | 启动