抽象组成
- Logger: 写日志接口,把要记录的日志发给它就好,要不要打印,打到哪里,用什么格式都被封装进去了.
- Handler: 用于封装一个日志写出方向
- 写console的handler: logging.StreamHandler()
- 写文件的handler: 如 handlers.TimedRotatingFileHandler
- Formatter: 用于定义一个日志打印格式, 绑定在一个handler上
其中,%(asctime)s用于控制日志中时间在哪个位置显示,
时间格式由datefmt参数控制,但毫秒不在asctime中控制;
%(msecs)d 用于毫秒部分的显示控制,d是显示几位毫秒
日志格式: https://docs.python.org/3.11/library/logging.html#logrecord-attributes
时间格式: https://docs.python.org/3.11/library/time.html#time.strftime - Filter: 用于定义一个日志过滤条件
这几个之间具有这样的组合关系
image.png
logger执行流程
logging 执行流程Logger 和 Handler 都可以 setLevel, 都可以 addFilter
日志等级(不过多展开了)
- 是个整数, 越大等级越高
- 默认设置打印等级后,等于和高于打印等级的日志会被打印
- logging. NOTSET < logging.DEBUG < logging.INFO < logging.WARNING < logging.ERROR < logging.CRITICAL
logger层级(todo: 下一篇)
其中名称为 "root" 的 logger 比较特殊,是根 logger
用法Demo
import logging
import os
import sys
from logging import handlers
# 日志目录
log_dir = os.getenv("app_logs", "./logs/")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
info_log_name = "info.log"
error_log_name = "error.log"
# 日志格式
log_fmt = "%(asctime)s.%(msecs)03d [%(process)d-%(thread)d] %(filename)s:%(lineno)d %(funcName)s " \
"[%(levelname)s] %(message)s"
date_fmt = "%Y-%m-%d %H:%M:%S"
formatter = logging.Formatter(fmt=log_fmt, datefmt=date_fmt)
# filter
# 只保留非错误级别的日志
class NormalLogFilter(logging.Filter):
def __init__(self, name):
super().__init__(name=name)
def filter(self, record: logging.LogRecord) -> bool:
if record.levelno < logging.ERROR:
return True
return False
# handler
# 打印到控制台(默认输出到标准错误流 sys.stderr)
console_handler = logging.StreamHandler(stream=sys.stdout)
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.DEBUG)
# info 日志打印到文件
info_log_file_handler = handlers.TimedRotatingFileHandler(
filename=os.path.join(log_dir, info_log_name), when="midnight", backupCount=15, encoding="utf-8")
info_log_file_handler.setFormatter(formatter)
info_log_file_handler.setLevel(logging.INFO)
normal_filter = NormalLogFilter("normalFilter")
info_log_file_handler.addFilter(normal_filter)
# error 日志打印到文件
error_log_file_handler = handlers.TimedRotatingFileHandler(
filename=os.path.join(log_dir, error_log_name), when="midnight", backupCount=15, encoding="utf-8")
error_log_file_handler.setFormatter(formatter)
error_log_file_handler.setLevel(logging.ERROR)
# 生成 logger
logger = logging.getLogger("root")
logger.addHandler(console_handler)
logger.addHandler(info_log_file_handler)
logger.addHandler(error_log_file_handler)
logger.setLevel(logging.INFO)
# ========= test code ======== #
def _log_use_test():
logger.setLevel(logging.DEBUG)
logger.debug("dddebug")
logger.info("iiinfo")
logger.warning("wwwarning")
logger.error("eeerror")
logger.critical("cccritical")
logger.info("占位符用法: %s:%d, 哈哈哈:%f", "词语", 12, 3.01)
word, idx, weight = "中文", 23, 5.23
logger.info(f"占位符用法: {word}:{idx}, 哈哈哈:{weight}")
try:
12 / 0
except Exception as e:
# 使用 logging 参数输出异常
logger.error("Houston, we have a %s", "major problem", exc_info=True)
try:
12 / 0
except Exception as e:
# 直接使用 logging 的 exception 函数输出异常堆栈信息
logger.exception("Houston, we have another %s. error= {}".format(e), "major problem")
try:
12 / 0
except Exception as e:
import traceback
s = traceback.format_exc()
# 使用 logging + traceback 模块输出异常
logging.info("traceback: {}".format(s))
if __name__ == "__main__":
_log_use_test()
生成的日志格式如下
2024-01-29 17:35:11.666 [18827-4788831744] log_conf.py:76 _log_use_test [DEBUG] dddebug
2024-01-29 17:35:11.667 [18827-4788831744] log_conf.py:77 _log_use_test [INFO] iiinfo
2024-01-29 17:35:11.669 [18827-4788831744] log_conf.py:78 _log_use_test [WARNING] wwwarning
2024-01-29 17:35:11.669 [18827-4788831744] log_conf.py:79 _log_use_test [ERROR] eeerror
2024-01-29 17:35:11.670 [18827-4788831744] log_conf.py:80 _log_use_test [CRITICAL] cccritical
2024-01-29 17:35:11.670 [18827-4788831744] log_conf.py:81 _log_use_test [INFO] 占位符用法: 词语:12, 哈哈哈:3.010000
2024-01-29 17:35:11.671 [18827-4788831744] log_conf.py:83 _log_use_test [INFO] 占位符用法: 中文:23, 哈哈哈:5.23
2024-01-29 17:35:11.671 [18827-4788831744] log_conf.py:89 _log_use_test [ERROR] Houston, we have a major problem
Traceback (most recent call last):
File "/Users/admin/git-repo/log_conf.py", line 86, in _log_use_test
12 / 0
ZeroDivisionError: division by zero
2024-01-29 17:35:11.673 [18827-4788831744] log_conf.py:95 _log_use_test [ERROR] Houston, we have another major problem. error= division by zero
Traceback (most recent call last):
File "/Users/admin/git-repo/log_conf.py", line 92, in _log_use_test
12 / 0
ZeroDivisionError: division by zero
2024-01-29 17:35:11.673 [18827-4788831744] log_conf.py:103 _log_use_test [INFO] traceback: Traceback (most recent call last):
File "/Users/admin/git-repo/log_conf.py", line 98, in _log_use_test
12 / 0
ZeroDivisionError: division by zero
- logging 模块解析: https://www.cnblogs.com/nancyzhu/p/8551506.html
- 自定义 Filter 用法: https://blog.csdn.net/B11050729/article/details/132400394
- Python官方 Logging HOWTO: https://docs.python.org/3/howto/logging.html
网友评论