1. 引言
在日常开发中,大家经常使用 print
函数来调试我们写的的代码。然而,随着打印语句数量的增加,由于缺乏行号或函数名称,很难确定输出来自何处。而且随着print语句的增多,调试完代码删除这些信息的时候也比较麻烦。
Python中的logging
模块为这一问题提供了完美的解决方案,允许大家为相应的输出指定不同的级别(DEBUG
、INFO
、WARNING
、ERROR
)。由于日志中包含时间戳、函数名称和行号等附加信息,可以帮助大家快速确定日志信息的来源。而且还可以将日志级别设置为 "INFO "
或更高,以排除调试日志,从而保持日志的简洁性和相关性。
本文重点介绍Python开源库 Loguru
的基本使用方法,以及这些特性如何使其成为标准日志库的logging
模块的绝佳替代品。
2. 开箱即用特性
一般情况下,标准日志库logging
模块的输出比较朴素平淡无奇,而Loguru
默认生成的日志内容丰富、颜色鲜明。
我们来看二者的对比,首先是标准日志库的默认输出:
import logging
logger = logging.getLogger(__name__)
def main():
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
if __name__ == "__main__":
main()
运行上述代码,结果如下:
接着是我们Loguru
库的默认输出:
from loguru import logger
def main():
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
if __name__ == "__main__":
main()
运行上述代码,结果如下:
可以直观的对比下上述两种输出,看看你更喜欢哪种?
3. 控制输出格式
有的同学看了上述的输出后,会说事实上logging
模块也可以控制指定输出格式,比如指定诸如时间戳、日志级别、模块名称、函数名称和行号。代码如下:
import logging
# Create a logger and set the logging level
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(module)s:%(funcName)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
def main():
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
if __name__ == "__main__":
main()
运行上述代码,结果如下:
可以看到,传统的日志记录方法使用 %
制定格式,使用起来并不直观;与之对比,Loguru
使用的{}
来指定格式更易读、更易用:
from loguru import logger
logger.add(
sys.stdout,
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {module}:{function}:{line} - {message}",
)
4. 将日志保存到文件
使用传统日志模块logging
将日志保存到文件并打印到终端需要两个额外的类 FileHandler
和 StreamHandler
, 示例代码如下:
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s | %(levelname)s | %(module)s:%(funcName)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[
logging.FileHandler(filename="info.log", level=logging.INFO),
logging.StreamHandler(level=logging.DEBUG),
],
)
logger = logging.getLogger(__name__)
def main():
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
if __name__ == "__main__":
main()
不过,有了 Loguru
之后,大家只需使用add
函数就能实现相同的功能。代码如下:
from loguru import logger
logger.add(
'info.log',
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {module}:{function}:{line} - {message}",
level="INFO",
)
def main():
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
if __name__ == "__main__":
main()
5. 日志过滤
日志过滤功能可以让大家根据特定条件有选择性地控制输出哪些日志记录。在日志库logging
中,过滤日志需要创建一个自定义日志过滤类。
import logging
logging.basicConfig(
filename="hello.log",
format="%(asctime)s | %(levelname)-8s | %(module)s:%(funcName)s:%(lineno)d - %(message)s",
level=logging.INFO,
)
class CustomFilter(logging.Filter):
def filter(self, record):
return "Hello" in record.msg
# Create a custom logging filter
custom_filter = CustomFilter()
# Get the root logger and add the custom filter to it
logger = logging.getLogger()
logger.addFilter(custom_filter)
def main():
logger.info("Hello World")
logger.info("Bye World")
if __name__ == "__main__":
main()
对比在Loguru
中,我们只需使用 lambda
函数即可实现同样的过滤功能,代码如下:
from loguru import logger
logger.add("hello.log", filter=lambda x: "Hello" in x["message"], level="INFO")
def main():
logger.info("Hello World")
logger.info("Bye World")
if __name__ == "__main__":
main()
运行上述代码后,我们可以在终端看到以下输出:
同时在日志文件hello.log
中,只保存我们过滤后的信息,如下所示:
6. 总结
虽然将Loguru
纳入项目需要使用pip install loguru
来额外进行安装,但它非常轻便,占用的磁盘空间也很小。此外,它还有助于减少记日志的代码量,而且使用起来更加直观和便捷,推荐大家在日常工作中多多使用!
您学废了嘛?