likes
comments
collection
share

python: 封装日志类确保记录日志的位置准确在开发大型应用程序时,日志记录是至关重要的工具,它帮助开发者调试代码、监

作者站长头像
站长
· 阅读数 48

在开发大型应用程序时,日志记录是至关重要的工具,它帮助开发者调试代码、监控系统行为以及排查问题。Python的标准库中提供了一个功能强大的logging模块,然而,直接使用logging模块往往会使代码变得冗长且难以维护。为了解决这个问题,我们可以封装一个日志类,以简化日志记录的操作,同时确保日志记录的位置(如文件名和行号)能够准确反映问题发生的上下文。

接下来将详细讲解如何通过一个封装类来实现这一目标。

python: 封装日志类确保记录日志的位置准确在开发大型应用程序时,日志记录是至关重要的工具,它帮助开发者调试代码、监

1. 封装日志类的初衷

在一个应用程序中,如果直接在每个需要记录日志的地方使用logging模块的基本功能,通常会产生以下问题:

  1. 代码冗长且重复:每次记录日志时都需要编写相同的代码来获取logger对象并设置相关参数。
  2. 日志记录位置不准确:如果将日志记录的功能封装在一个类中,由于日志记录的调用总是发生在这个封装类中,日志中记录的文件名和行号信息往往指向封装类本身,而不是实际调用该日志记录函数的地方。

为了克服上述问题,我们可以设计一个封装类,在封装logging模块的同时,确保日志记录位置的准确性。

2. 代码实现解析

我们以如下封装类为例进行解析:

import logging
import logging.config
import inspect

class Log(object):
    logging.config.fileConfig('/etc/logging.conf')
    __logger = logging.getLogger('script')

    @classmethod
    def set_level(cls, level):
        cls.__logger.setLevel(level)

    @classmethod
    def debug(cls, msg, *args, **kwargs):
        cls._log(logging.DEBUG, msg, *args, **kwargs)

    @classmethod    
    def info(cls, msg, *args, **kwargs):
        cls._log(logging.INFO, msg, *args, **kwargs)

    @classmethod    
    def warning(cls, msg, *args, **kwargs):
        cls._log(logging.WARNING, msg, *args, **kwargs)

    @classmethod    
    def error(cls, msg, *args, **kwargs):
        cls._log(logging.ERROR, msg, *args, **kwargs)

    @classmethod    
    def critical(cls, msg, *args, **kwargs):
        cls._log(logging.CRITICAL, msg, *args, **kwargs)
        
    @classmethod
    def _log(cls, level, msg, *args, **kwargs):
        # 获取调用者的信息
        frame = inspect.currentframe().f_back.f_back
        caller = inspect.getframeinfo(frame)
        # 记录日志,传入调用者的文件名和行号
        cls.__logger.log(level, msg, *args, **kwargs, extra={'filename': caller.filename, 'lineno': caller.lineno})

    @classmethod
    def get_logger(cls):
        return cls.__logger

3. 关键代码讲解

3.1 使用inspect模块获取调用者信息

为了确保日志记录的位置(文件名和行号)准确,我们使用了Python的inspect模块。inspect模块允许我们在运行时检查当前的堆栈帧,从而获取调用者的信息。

_log方法中,我们使用了以下代码:

frame = inspect.currentframe().f_back.f_back
caller = inspect.getframeinfo(frame)
  • inspect.currentframe():返回当前的堆栈帧。
  • f_back:获取调用该函数的上一层堆栈帧。
  • f_back.f_back:获取调用_log函数的上一层堆栈帧,通常就是实际调用日志记录方法的地方。

通过调用inspect.getframeinfo(frame),我们能够获取调用者的文件名和行号信息。最终,这些信息会通过extra参数传递给logger.log方法,用于在日志中记录。

3.2 封装日志方法

在封装类中,我们定义了一系列的类方法(如debuginfowarning等)来对应不同的日志级别。这些方法内部统一调用了_log方法,并将日志级别作为参数传入。这种设计确保了调用者在使用该封装类时,可以通过简洁的代码记录日志,而无需关心底层实现的细节。

3.3 日志记录配置的加载

在类的初始化部分,使用logging.config.fileConfig方法加载了日志配置文件。这保证了日志的配置只会被加载一次,避免了重复加载配置文件的开销。此外,日志配置的加载放在类的初始化部分,也意味着日志类的实例在任何时候都可以直接使用,无需再次配置。

4. 实现的效果

通过上述封装,我们实现了以下几个目标:

  1. 简化日志记录操作:开发者在需要记录日志的地方,只需调用封装类的方法,而不必每次手动获取logger对象或设置相关参数。
  2. 确保日志记录位置准确:通过inspect模块获取调用者信息,日志记录中的文件名和行号准确反映了问题发生的位置,而不是指向封装类本身。
  3. 配置一次,全局可用:日志配置只需在初始化时加载一次,封装类的实例在任何地方都可以直接使用相同的配置进行日志记录。

5. 总结

封装logging模块的一个关键挑战在于如何确保日志记录的位置信息准确。在本文的示例中,通过使用Python的inspect模块,我们成功解决了这一问题。封装日志类不仅简化了日志记录的操作,还确保了日志信息的准确性,提升了代码的可读性和可维护性。

这种设计思路在大型项目中尤为重要,因为它可以显著减少调试和维护的难度,同时提供一致的日志记录机制。

转载自:https://juejin.cn/post/7410239025892196415
评论
请登录