文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

为什么不推荐使用Python原生日志库?

2024-11-30 06:04

关注

Python自带的logging我个人不推介使用,不太Pythonic,而开源的Loguru库成为众多工程师及项目中首选,本期将同时对logging及Loguru进行使用对比,希望有所帮助。

快速示例

在logging中,默认的日志功能输出的信息较为有限:

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()

输出(logging默认日志等级为warning,故此处未输出info与debug等级的信息):

WARNING:root:This is a warning message
ERROR:root:This is an error message

再来看看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()

提供了执行时间、等级、在哪个函数调用、具体哪一行等信息。

格式化日志

格式化日志允许我们向日志添加有用的信息,例如时间戳、日志级别、模块名称、函数名称和行号。

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

输出:

2023-10-18 15:47:30 | INFO | tmp::186 - This is an info message
2023-10-18 15:47:30 | WARNING | tmp::187 - This is a warning message
2023-10-18 15:47:30 | ERROR | tmp::188 - This is an error message

而loguru使用和f-string相同的{}格式,更方便:

from loguru import logger

logger.add(
    sys.stdout,
    level="INFO",
    format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {module}:{function}:{line} - {message}",
)

日志保存

在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="/your/save/path/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()

日志轮换

日志轮换指通过定期创建新的日志文件并归档或删除旧的日志来防止日志变得过大。

在logging中,需要一个名为 TimedRotatingFileHandler 的附加类,以下代码示例代表每周切换到一个新的日志文件 ( when=“WO”, interval=1 ),并保留最多 4 周的日志文件 ( backupCount=4 ):

import logging
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Create a formatter with the desired log format
formatter = logging.Formatter(
    "%(asctime)s | %(levelname)-8s | %(module)s:%(funcName)s:%(lineno)d - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

file_handler = TimedRotatingFileHandler(
    filename="debug2.log", when="WO", interval=1, backupCount=4
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)


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中,可以通过将 rotation 和 retention 参数添加到 add 方法来达到目的,如下示例,同样肥肠方便:

from loguru import logger

logger.add("debug.log", level="INFO", rotation="1 week", retention="4 weeks")


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()

日志筛选

日志筛选指根据特定条件有选择的控制应输出与保存哪些日志信息。

在logging中,实现该功能需要创建自定义日志过滤器类:

import logging


logging.basicConfig(
    filename="test.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 "Cai Xukong" 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 Cai Xukong")
    logger.info("Bye Cai Xukong")


if __name__ == "__main__":
    main()

在loguru中,可以简单地使用lambda函数来过滤日志:

from loguru import logger

logger.add("test.log", filter=lambda x: "Cai Xukong" in x["message"], level="INFO")


def main():
    logger.info("Hello Cai Xukong")
    logger.info("Bye Cai Xukong")


if __name__ == "__main__":
    main()

捕获异常

在logging中捕获异常较为不便且难以调试,如:

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


def division(a, b):
    return a / b


def nested(c):
    try:
        division(1, c)
    except ZeroDivisionError:
        logging.exception("ZeroDivisionError")


if __name__ == "__main__":
    nested(0)
Traceback (most recent call last):
  File "logging_example.py", line 16, in nested
    division(1, c)
  File "logging_example.py", line 11, in division
    return a / b
ZeroDivisionError: division by zero

上面输出的信息未提供触发异常的c值信息,而在loguru中,通过显示包含变量值的完整堆栈跟踪来方便用户识别:

Traceback (most recent call last):
  File "logging_example.py", line 16, in nested
    division(1, c)
  File "logging_example.py", line 11, in division
    return a / b
ZeroDivisionError: division by zero

值得一提的是,loguru中的catch装饰器允许用户捕获函数内任何错误,且还会标识发生错误的线程:

from loguru import logger


def division(a, b):
    return a / b


@logger.catch
def nested(c):
    division(1, c)


if __name__ == "__main__":
    nested(0)

OK,作为普通玩家以上功能足以满足日常日志需求,通过对比logging与loguru应该让大家有了直观感受,哦对了,loguru如何安装?

pip install loguru

以上就是本期的全部内容,期待点赞在看,我是啥都生,下次再见。

来源:啥都会一点的研究生内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯