日志记录是跟踪和记录应用程序中发生的关键事件的过程,用于检查、调试等。它们比print
语句更强大,因为它们允许将特定的信息片段发送到具有自定义功能的特定位置格式化、共享接口等。这使得日志记录成为能够从应用程序的内部流程中发现有洞察力的信息的关键支持者。
有几个总体概念需要注意:
Logger
: 从应用程序发出日志消息。
Handler
:将日志记录发送到特定位置。
Formatter
: 格式化和样式化日志记录。
日志记录还有很多,例如过滤器、异常日志记录等,但这些基础知识将使能够为应用程序做需要的一切。
在创建专门的配置记录器之前,让看看使用基本配置记录的消息是什么样子的。
import logging
import sys
# Create super basic logger
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
# Logging levels (from lowest to highest priority)
logging.debug("Used for debugging your code.")
logging.info("Informative messages from your code.")
logging.warning("Everything works but there is something to be aware of.")
logging.error("There's been a mistake with the process.")
logging.critical("There is something terribly wrong and process may terminate.")
DEBUG:root:Used for debugging your code.
INFO:root:Informative messages from your code.
WARNING:root:Everything works but there is something to be aware of.
ERROR:root:There's been a mistake with the process.
CRITICAL:root:There is something terribly wrong and process may terminate.
这些是日志记录的基本级别,其中DEBUG
优先级最低,优先级CRITICAL
最高。定义了记录器,basicConfig
用于将日志消息发送到标准输出(即终端控制台),但也可以写入任何其他流甚至文件。还将日志定义为对从 level 开始的日志消息敏感DEBUG
。这意味着所有记录的消息都将显示,因为DEBUG
它是最低级别。如果设置了 level ,ERROR
那么只会显示日志消息。
import logging
import sys
# Create super basic logger
logging.basicConfig(stream=sys.stdout, level=logging.ERROR)
# Logging levels (from lowest to highest priority)
logging.debug("Used for debugging your code.")
logging.info("Informative messages from your code.")
logging.warning("Everything works but there is something to be aware of.")
logging.error("There's been a mistake with the process.")
logging.critical("There is something terribly wrong and process may terminate.")
ERROR:root:There's been a mistake with the process.
CRITICAL:root:There is something terribly wrong and process may terminate.
首先,将在config.py
脚本中设置日志的位置:
# config/config.py
LOGS_DIR = Path(BASE_DIR, "logs")
LOGS_DIR.mkdir(parents=True, exist_ok=True)
接下来,将为应用程序配置记录器:
# config/config.py
import logging
import sys
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"minimal": {"format": "%(message)s"},
"detailed": {
"format": "%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "minimal",
"level": logging.DEBUG,
},
"info": {
"class": "logging.handlers.RotatingFileHandler",
"filename": Path(LOGS_DIR, "info.log"),
"maxBytes": 10485760, # 1 MB
"backupCount": 10,
"formatter": "detailed",
"level": logging.INFO,
},
"error": {
"class": "logging.handlers.RotatingFileHandler",
"filename": Path(LOGS_DIR, "error.log"),
"maxBytes": 10485760, # 1 MB
"backupCount": 10,
"formatter": "detailed",
"level": logging.ERROR,
},
},
"root": {
"handlers": ["console", "info", "error"],
"level": logging.INFO,
"propagate": True,
},
}
[Lines 6-11]
:定义两个不同的
Formatters(确定日志消息的格式和样式),minimal 和 detailed,它们使用各种
LogRecord 属性为日志消息创建格式化模板。
[Lines 12-35]
:定义不同的
处理程序(有关发送日志消息的位置的详细信息):
console
:将日志消息(使用
minimal
格式化程序)发送到
stdout
高于级别的消息流
DEBUG
(即所有记录的消息)。
info
:将日志消息(使用
detailed
格式化程序)发送到
logs/info.log
(一个可以达到的文件,
1 MB
将备份
10
它的最新版本)以获得级别以上的消息
INFO
。
error
:将日志消息(使用
detailed
格式化程序)发送到
logs/error.log
(一个可以达到的文件,
1 MB
将备份
10
它的最新版本)以获得级别以上的消息
ERROR
。
[Lines 36-40]
:将不同的处理程序附加到根
Logger。
选择使用字典来配置记录器,但还有其他方法,例如 Python 脚本、配置文件等。单击下面的不同选项以展开并查看各自的实现。
Python 脚本
import logging
from rich.logging import RichHandler
- 1
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
console_handler = RichHandler(markup=True)
console_handler.setLevel(logging.DEBUG)
info_handler = logging.handlers.RotatingFileHandler(
filename=Path(LOGS_DIR, "info.log"),
maxBytes=10485760, # 1 MB
backupCount=10,
)
info_handler.setLevel(logging.INFO)
error_handler = logging.handlers.RotatingFileHandler(
filename=Path(LOGS_DIR, "error.log"),
maxBytes=10485760, # 1 MB
backupCount=10,
)
error_handler.setLevel(logging.ERROR)
minimal_formatter = logging.Formatter(fmt="%(message)s")
detailed_formatter = logging.Formatter(
fmt="%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
)
console_handler.setFormatter(fmt=minimal_formatter)
info_handler.setFormatter(fmt=detailed_formatter)
error_handler.setFormatter(fmt=detailed_formatter)
logger.addHandler(hdlr=console_handler)
logger.addHandler(hdlr=info_handler)
logger.addHandler(hdlr=error_handler)
import logging
from rich.logging import RichHandler
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
console_handler = RichHandler(markup=True)
console_handler.setLevel(logging.DEBUG)
info_handler = logging.handlers.RotatingFileHandler(
filename=Path(LOGS_DIR, "info.log"),
maxBytes=10485760, # 1 MB
backupCount=10,
)
info_handler.setLevel(logging.INFO)
error_handler = logging.handlers.RotatingFileHandler(
filename=Path(LOGS_DIR, "error.log"),
maxBytes=10485760, # 1 MB
backupCount=10,
)
error_handler.setLevel(logging.ERROR)
minimal_formatter = logging.Formatter(fmt="%(message)s")
detailed_formatter = logging.Formatter(
fmt="%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
)
console_handler.setFormatter(fmt=minimal_formatter)
info_handler.setFormatter(fmt=detailed_formatter)
error_handler.setFormatter(fmt=detailed_formatter)
logger.addHandler(hdlr=console_handler)
logger.addHandler(hdlr=info_handler)
logger.addHandler(hdlr=error_handler)
把它放在一个logging.config
文件中:
[formatters]
keys=minimal,detailed
[formatter_minimal]
format=%(message)s
[formatter_detailed]
format=
%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]
%(message)s
[handlers]
keys=console,info,error
[handler_console]
class=StreamHandler
level=DEBUG
formatter=minimal
args=(sys.stdout,)
[handler_info]
class=handlers.RotatingFileHandler
level=INFO
formatter=detailed
backupCount=10
maxBytes=10485760
args=("logs/info.log",)
[handler_error]
class=handlers.RotatingFileHandler
level=ERROR
formatter=detailed
backupCount=10
maxBytes=10485760
args=("logs/error.log",)
[loggers]
keys=root
[logger_root]
level=INFO
handlers=console,info,error
把它放在你的 Python 脚本中
import logging
import logging.config
from rich.logging import RichHandler
# Use config file to initialize logger
logging.config.fileConfig(Path(CONFIG_DIR, "logging.config"))
logger = logging.getLogger()
logger.handlers[0] = RichHandler(markup=True) # set rich handler
可以像这样加载记录器配置字典:
# config/config.py
from rich.logging import RichHandler
logging.config.dictConfig(logging_config)
logger = logging.getLogger()
logger.handlers[0] = RichHandler(markup=True) # pretty formatting
# Sample messages (note that we use configured `logger` now)
logger.debug("Used for debugging your code.")
logger.info("Informative messages from your code.")
logger.warning("Everything works but there is something to be aware of.")
logger.error("There's been a mistake with the process.")
logger.critical("There is something terribly wrong and process may terminate.")
DEBUG Used for debugging your code. config.py:71
INFO Informative messages from your code. config.py:72
WARNING Everything works but there is something to be aware of. config.py:73
ERROR There's been a mistake with the process. config.py:74
CRITICAL There is something terribly wrong and process may terminate. config.py:75
使用RichHandler作为console
处理程序来为日志消息获取漂亮的格式。这不是预安装的库,因此需要安装并添加到requirements.txt
:
pip install rich==12.4.4
# Add to requirements.txt rich==12.4.4
记录的消息存储在日志目录中的相应文件中:
logs/
├── info.log
└── error.log
由于定义了详细的格式化程序,因此会看到如下信息丰富的日志消息:
在项目中,可以将所有打印语句替换为日志语句:
print("✅ Saved data!")
──── 变成:────
from config.config import logger
logger.info("✅ Saved data!")
所有的日志消息都在INFO
级别上,但在开发过程中可能不得不使用级别,如果系统以意外的方式运行DEBUG
,还会添加一些ERROR
或日志消息。CRITICAL
what:记录您希望从应用程序中显示的所有必要详细信息,这些详细信息将在开发期间和之后的回顾性检查中有用。
其中:最佳做法是不要将模块化函数与日志语句混为一谈。相反,应该在小函数之外和更大的工作流程中记录消息。例如,除了main.py
和train.py
文件之外,任何脚本中都没有日志消息。这是因为这些脚本使用了其他脚本(data.py、evaluate.py 等)中定义的较小函数。如果觉得需要在其他功能中登录,那么它通常表明该功能需要进一步分解。
Elastic stack(以前称为 ELK stack)是生产级日志记录的常用选项。它结合了Elasticsearch(分布式搜索引擎)、Logstash(摄取管道)和Kibana(可定制可视化)的特性。也可以简单地将日志上传到云博客存储(例如 S3、谷歌云存储等)。
本文主体源自以下链接:
@article{madewithml,
author = {Goku Mohandas},
title = { Made With ML },
howpublished = {\url{https://madewithml.com/}},
year = {2022}
}
本文由 mdnice 多平台发布