正在进入ing...

Python 标准库 Logging 学习笔记

发布时间:2023-07-02 浏览量: 1116 文章分类: python

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",
)

这里面的参数还挺多的,具体可以查阅模块方法。主要就是下面这几个方法。filenamefilemodeformatdatefmtstylelevelstreamhandlersforceencodingerrors

高级应用

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使用的。因为他可以使用jsonxml数据库存储等任何能够转换成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 | 启动